Merge pull request #4327 from vkargov/vk-abcremedy
[mono.git] / mono / metadata / mono-perfcounters.c
1 /**
2  * \file
3  *
4  * Performance counters support.
5  *
6  * Author: Paolo Molaro (lupus@ximian.com)
7  *
8  * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
9  * 2011 Xamarin, Inc
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #include "config.h"
14 #include <time.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #if defined (__OpenBSD__)
21 #include <sys/param.h>
22 #endif
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #ifdef HAVE_SYS_TIME_H
27 #include <sys/time.h>
28 #endif
29 #if defined (__APPLE__)
30 #include <mach/message.h>
31 #include <mach/mach_host.h>
32 #include <mach/host_info.h>
33 #include <sys/sysctl.h>
34 #endif
35 #if defined (__NetBSD__)
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <sys/vmmeter.h>
39 #endif
40 #include "metadata/mono-perfcounters.h"
41 #include "metadata/appdomain.h"
42 #include "metadata/object-internals.h"
43 /* for mono_stats */
44 #include "metadata/class-internals.h"
45 #include "utils/mono-time.h"
46 #include "utils/mono-mmap.h"
47 #include "utils/mono-proclib.h"
48 #include "utils/mono-networkinterfaces.h"
49 #include "utils/mono-error-internals.h"
50 #include "utils/atomic.h"
51
52 /* map of CounterSample.cs */
53 struct _MonoCounterSample {
54         gint64 rawValue;
55         gint64 baseValue;
56         gint64 counterFrequency;
57         gint64 systemFrequency;
58         gint64 timeStamp;
59         gint64 timeStamp100nSec;
60         gint64 counterTimeStamp;
61         int counterType;
62 };
63
64 #ifndef DISABLE_PERFCOUNTERS
65 /* map of PerformanceCounterType.cs */
66 enum {
67         NumberOfItemsHEX32=0x00000000,
68         NumberOfItemsHEX64=0x00000100,
69         NumberOfItems32=0x00010000,
70         NumberOfItems64=0x00010100,
71         CounterDelta32=0x00400400,
72         CounterDelta64=0x00400500,
73         SampleCounter=0x00410400,
74         CountPerTimeInterval32=0x00450400,
75         CountPerTimeInterval64=0x00450500,
76         RateOfCountsPerSecond32=0x10410400,
77         RateOfCountsPerSecond64=0x10410500,
78         RawFraction=0x20020400,
79         CounterTimer=0x20410500,
80         Timer100Ns=0x20510500,
81         SampleFraction=0x20C20400,
82         CounterTimerInverse=0x21410500,
83         Timer100NsInverse=0x21510500,
84         CounterMultiTimer=0x22410500,
85         CounterMultiTimer100Ns=0x22510500,
86         CounterMultiTimerInverse=0x23410500,
87         CounterMultiTimer100NsInverse=0x23510500,
88         AverageTimer32=0x30020400,
89         ElapsedTime=0x30240500,
90         AverageCount64=0x40020500,
91         SampleBase=0x40030401,
92         AverageBase=0x40030402,
93         RawBase=0x40030403,
94         CounterMultiBase=0x42030500
95 };
96
97 /* maps a small integer type to the counter types above */
98 static const int
99 simple_type_to_type [] = {
100         NumberOfItemsHEX32, NumberOfItemsHEX64,
101         NumberOfItems32, NumberOfItems64,
102         CounterDelta32, CounterDelta64,
103         SampleCounter, CountPerTimeInterval32,
104         CountPerTimeInterval64, RateOfCountsPerSecond32,
105         RateOfCountsPerSecond64, RawFraction,
106         CounterTimer, Timer100Ns,
107         SampleFraction, CounterTimerInverse,
108         Timer100NsInverse, CounterMultiTimer,
109         CounterMultiTimer100Ns, CounterMultiTimerInverse,
110         CounterMultiTimer100NsInverse, AverageTimer32,
111         ElapsedTime, AverageCount64,
112         SampleBase, AverageBase,
113         RawBase, CounterMultiBase
114 };
115
116 enum {
117         SingleInstance,
118         MultiInstance,
119         CatTypeUnknown = -1
120 };
121
122 enum {
123         ProcessInstance,
124         ThreadInstance,
125         CPUInstance,
126         MonoInstance,
127         NetworkInterfaceInstance,
128         CustomInstance
129 };
130
131 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
132 #define PERFCTR_COUNTER(id,name,help,type,field)
133 enum {
134 #include "mono-perfcounters-def.h"
135         NUM_CATEGORIES
136 };
137
138 #undef PERFCTR_CAT
139 #undef PERFCTR_COUNTER
140 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
141 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
142 /* each counter is assigned an id starting from 0 inside the category */
143 enum {
144 #include "mono-perfcounters-def.h"
145         END_COUNTERS
146 };
147
148 #undef PERFCTR_CAT
149 #undef PERFCTR_COUNTER
150 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
151 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
152 /* this is used just to count the number of counters */
153 enum {
154 #include "mono-perfcounters-def.h"
155         NUM_COUNTERS
156 };
157
158 static mono_mutex_t perfctr_mutex;
159 #define perfctr_lock() mono_os_mutex_lock (&perfctr_mutex)
160 #define perfctr_unlock() mono_os_mutex_unlock (&perfctr_mutex)
161
162 typedef struct {
163         char reserved [16];
164         int size;
165         unsigned short counters_start;
166         unsigned short counters_size;
167         unsigned short data_start;
168         MonoPerfCounters counters;
169         char data [1];
170 } MonoSharedArea;
171
172 /*
173   binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
174   basic stanza:
175   struct stanza_header {
176         byte stanza_type; // FTYPE_*
177         byte other_info;
178         ushort stanza_length; // includeas header
179         ... data ...
180   }
181
182 // strings are utf8
183 // perfcat and perfinstance are 4-bytes aligned
184 struct perfcat {
185         byte typeidx;
186         byte categorytype;
187         ushort length; // includes the counters
188         ushort num_counters;
189         ushort counters_data_size;
190         int num_instances;
191         char name[]; // null terminated
192         char help[]; // null terminated
193         // perfcounters follow
194         {
195                 byte countertype;
196                 char name[]; // null terminated
197                 char help[]; // null terminated
198         }
199         0-byte
200 };
201
202 struct perfinstance {
203         byte typeidx;
204         byte data_offset; // offset of counters from beginning of struct
205         ushort length;
206         uint category_offset; // offset of category in the shared area
207         char name[]; // null terminated
208         // data follows: this is always 8-byte aligned
209 };
210
211 */
212
213 enum {
214         FTYPE_CATEGORY = 'C',
215         FTYPE_DELETED = 'D',
216         FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
217         FTYPE_INSTANCE = 'I',
218         FTYPE_DIRTY = 'd',
219         FTYPE_END = 0
220 };
221
222 typedef struct {
223         unsigned char ftype;
224         unsigned char extra;
225         unsigned short size;
226 } SharedHeader;
227
228 typedef struct {
229         SharedHeader header;
230         unsigned short num_counters;
231         unsigned short counters_data_size;
232         int num_instances;
233         /* variable length data follows */
234         char name [1];
235         // string name
236         // string help
237         // SharedCounter counters_info [num_counters]
238 } SharedCategory;
239
240 typedef struct {
241         SharedHeader header;
242         unsigned int category_offset;
243         /* variable length data follows */
244         char instance_name [1];
245         // string name
246 } SharedInstance;
247
248 typedef struct {
249         unsigned char type;
250         guint8 seq_num;
251         /* variable length data follows */
252         char name [1];
253         // string name
254         // string help
255 } SharedCounter;
256
257 typedef struct {
258         const char *name;
259         const char *help;
260         unsigned char id;
261         signed int type : 2;
262         unsigned int instance_type : 6;
263         short first_counter;
264 } CategoryDesc;
265
266 typedef struct {
267         const char *name;
268         const char *help;
269         short id;
270         unsigned short offset; // offset inside MonoPerfCounters
271         int type;
272 } CounterDesc;
273
274 #undef PERFCTR_CAT
275 #undef PERFCTR_COUNTER
276 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
277 #define PERFCTR_COUNTER(id,name,help,type,field)
278 static const CategoryDesc
279 predef_categories [] = {
280 #include "mono-perfcounters-def.h"
281         {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
282 };
283
284 #undef PERFCTR_CAT
285 #undef PERFCTR_COUNTER
286 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
287 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
288 static const CounterDesc
289 predef_counters [] = {
290 #include "mono-perfcounters-def.h"
291         {NULL, NULL, -1, 0, 0}
292 };
293
294 /*
295  * We have several different classes of counters:
296  * *) system counters
297  * *) runtime counters
298  * *) remote counters
299  * *) user-defined counters
300  * *) windows counters (the implementation on windows will use this)
301  *
302  * To easily handle the differences we create a vtable for each class that contains the
303  * function pointers with the actual implementation to access the counters.
304  */
305 typedef struct _ImplVtable ImplVtable;
306
307 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
308 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
309 typedef void (*CleanupFunc) (ImplVtable *vtable);
310
311 struct _ImplVtable {
312         void *arg;
313         SampleFunc sample;
314         UpdateFunc update;
315         CleanupFunc cleanup;
316 };
317
318 typedef struct {
319         int id;
320         char *name;
321 } NetworkVtableArg;
322
323 typedef struct {
324         ImplVtable vtable;
325         MonoPerfCounters *counters;
326         int pid;
327 } PredefVtable;
328
329 typedef struct {
330         ImplVtable vtable;
331         SharedInstance *instance_desc;
332         SharedCounter *counter_desc;
333 } CustomVTable;
334
335 static ImplVtable*
336 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
337 {
338         ImplVtable *vtable = g_new0 (ImplVtable, 1);
339         vtable->arg = arg;
340         vtable->sample = sample;
341         vtable->update = update;
342         return vtable;
343 }
344
345 MonoPerfCounters *mono_perfcounters = NULL;
346 static MonoSharedArea *shared_area = NULL;
347
348 typedef struct {
349         void *sarea;
350         int refcount;
351 } ExternalSArea;
352
353 /* maps a pid to a ExternalSArea pointer */
354 static GHashTable *pid_to_shared_area = NULL;
355
356 static MonoSharedArea *
357 load_sarea_for_pid (int pid)
358 {
359         ExternalSArea *data;
360         MonoSharedArea *area = NULL;
361
362         perfctr_lock ();
363         if (pid_to_shared_area == NULL)
364                 pid_to_shared_area = g_hash_table_new (NULL, NULL);
365         data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
366         if (!data) {
367                 area = (MonoSharedArea *)mono_shared_area_for_pid (GINT_TO_POINTER (pid));
368                 if (area) {
369                         data = g_new (ExternalSArea, 1);
370                         data->sarea = area;
371                         data->refcount = 1;
372                         g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
373                 }
374         } else {
375                 area = (MonoSharedArea *)data->sarea;
376                 data->refcount ++;
377         }
378         perfctr_unlock ();
379         return area;
380 }
381
382 static void
383 unref_pid_unlocked (int pid)
384 {
385         ExternalSArea *data;
386         data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
387         if (data) {
388                 data->refcount--;
389                 if (!data->refcount) {
390                         g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
391                         mono_shared_area_unload (data->sarea);
392                         g_free (data);
393                 }
394         }
395 }
396
397 static void
398 predef_cleanup (ImplVtable *vtable)
399 {
400         PredefVtable *vt = (PredefVtable*)vtable;
401         /* ExternalSArea *data; */
402         
403         perfctr_lock ();
404         if (!pid_to_shared_area) {
405                 perfctr_unlock ();
406                 return;
407         }
408         unref_pid_unlocked (vt->pid);
409         perfctr_unlock ();
410 }
411
412 static guint64
413 mono_determine_physical_ram_size (void)
414 {
415 #if defined (TARGET_WIN32)
416         MEMORYSTATUSEX memstat;
417
418         memstat.dwLength = sizeof (memstat);
419         GlobalMemoryStatusEx (&memstat);
420         return (guint64)memstat.ullTotalPhys;
421 #elif defined (__NetBSD__) || defined (__APPLE__)
422 #ifdef __NetBSD__
423         unsigned long value;
424 #else
425         guint64 value;
426 #endif
427         int mib[2] = {
428                 CTL_HW,
429 #ifdef __NetBSD__
430                 HW_PHYSMEM64
431 #else
432                 HW_MEMSIZE
433 #endif
434         };
435         size_t size_sys = sizeof (value);
436
437         sysctl (mib, 2, &value, &size_sys, NULL, 0);
438         if (value == 0)
439                 return 134217728;
440
441         return (guint64)value;
442 #elif defined (HAVE_SYSCONF)
443         guint64 page_size = 0, num_pages = 0;
444
445         /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
446          * reports invalid values, please add your OS specific code below. */
447 #ifdef _SC_PAGESIZE
448         page_size = (guint64)sysconf (_SC_PAGESIZE);
449 #endif
450
451 #ifdef _SC_PHYS_PAGES
452         num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
453 #endif
454
455         if (!page_size || !num_pages) {
456                 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
457                 return 134217728;
458         }
459
460         return page_size * num_pages;
461 #else
462         return 134217728;
463 #endif
464 }
465
466 static guint64
467 mono_determine_physical_ram_available_size (void)
468 {
469 #if defined (TARGET_WIN32)
470         MEMORYSTATUSEX memstat;
471
472         memstat.dwLength = sizeof (memstat);
473         GlobalMemoryStatusEx (&memstat);
474         return (guint64)memstat.ullAvailPhys;
475
476 #elif defined (__NetBSD__)
477         struct vmtotal vm_total;
478         guint64 page_size;
479         int mib[2];
480         size_t len;
481
482         mib[0] = CTL_VM;
483         mib[1] = VM_METER;
484
485         len = sizeof (vm_total);
486         sysctl (mib, 2, &vm_total, &len, NULL, 0);
487
488         mib[0] = CTL_HW;
489         mib[1] = HW_PAGESIZE;
490
491         len = sizeof (page_size);
492         sysctl (mib, 2, &page_size, &len, NULL, 0);
493
494         return ((guint64) vm_total.t_free * page_size) / 1024;
495 #elif defined (__APPLE__)
496         mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
497         mach_port_t host = mach_host_self();
498         vm_size_t page_size;
499         vm_statistics_data_t vmstat;
500         kern_return_t ret;
501         do {
502                 ret = host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &count);
503         } while (ret == KERN_ABORTED);
504
505         if (ret != KERN_SUCCESS) {
506                 g_warning ("Mono was unable to retrieve memory usage!");
507                 return 0;
508         }
509
510         host_page_size(host, &page_size);
511         return (guint64) vmstat.free_count * page_size;
512
513 #elif defined (HAVE_SYSCONF)
514         guint64 page_size = 0, num_pages = 0;
515
516         /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
517          * reports invalid values, please add your OS specific code below. */
518 #ifdef _SC_PAGESIZE
519         page_size = (guint64)sysconf (_SC_PAGESIZE);
520 #endif
521
522 #ifdef _SC_AVPHYS_PAGES
523         num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
524 #endif
525
526         if (!page_size || !num_pages) {
527                 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
528                 return 0;
529         }
530
531         return page_size * num_pages;
532 #else
533         return 0;
534 #endif
535 }
536
537 void
538 mono_perfcounters_init (void)
539 {
540         int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
541         d_offset += 7;
542         d_offset &= ~7;
543
544         mono_os_mutex_init_recursive (&perfctr_mutex);
545
546         shared_area = (MonoSharedArea *)mono_shared_area ();
547         shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
548         shared_area->counters_size = sizeof (MonoPerfCounters);
549         shared_area->data_start = d_offset;
550         shared_area->size = 4096;
551         mono_perfcounters = &shared_area->counters;
552 }
553
554 static int
555 perfctr_type_compress (int type)
556 {
557         int i;
558         for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
559                 if (simple_type_to_type [i] == type)
560                         return i;
561         }
562         /* NumberOfItems32 */
563         return 2;
564 }
565
566 static SharedHeader*
567 shared_data_reserve_room (int size, int ftype)
568 {
569         SharedHeader* header;
570         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
571         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
572
573         size += 7;
574         size &= ~7;
575         while (p < end) {
576                 unsigned short *next;
577                 if (*p == FTYPE_END) {
578                         if (size < (end - p))
579                                 goto res;
580                         return NULL;
581                 }
582                 if (p + 4 > end)
583                         return NULL;
584                 next = (unsigned short*)(p + 2);
585                 if (*p == FTYPE_DELETED) {
586                         /* we reuse only if it's the same size */
587                         if (*next == size) {
588                                 goto res;
589                         }
590                 }
591                 p += *next;
592         }
593         return NULL;
594
595 res:
596         header = (SharedHeader*)p;
597         header->ftype = ftype;
598         header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
599         header->size = size;
600
601         return header;
602 }
603
604 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
605
606 static void
607 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
608 {
609         while (p < end) {
610                 unsigned short *next;
611                 if (p + 4 > end)
612                         return;
613                 next = (unsigned short*)(p + 2);
614                 if (!func ((SharedHeader*)p, data))
615                         return;
616                 if (*p == FTYPE_END)
617                         return;
618                 p += *next;
619         }
620 }
621
622 static void
623 foreach_shared_item (SharedFunc func, void *data)
624 {
625         unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
626         unsigned char *end = (unsigned char *)shared_area + shared_area->size;
627
628         foreach_shared_item_in_area (p, end, func, data);
629 }
630
631 static int
632 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
633 {
634         /* FIXME: make this case insensitive */
635         guint16 *strc = mono_string_chars (str);
636         while (*strc == *ascii_str++) {
637                 if (*strc == 0)
638                         return 0;
639                 strc++;
640         }
641         return *strc - *(const unsigned char *)(ascii_str - 1);
642 }
643
644 typedef struct {
645         MonoString *name;
646         SharedCategory *cat;
647 } CatSearch;
648
649 static gboolean
650 category_search (SharedHeader *header, void *data)
651 {
652         CatSearch *search = (CatSearch *)data;
653         if (header->ftype == FTYPE_CATEGORY) {
654                 SharedCategory *cat = (SharedCategory*)header;
655                 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
656                         search->cat = cat;
657                         return FALSE;
658                 }
659         }
660         return TRUE;
661 }
662
663 static SharedCategory*
664 find_custom_category (MonoString *name)
665 {
666         CatSearch search;
667         search.name = name;
668         search.cat = NULL;
669         foreach_shared_item (category_search, &search);
670         return search.cat;
671 }
672
673 static gboolean
674 category_collect (SharedHeader *header, void *data)
675 {
676         GSList **list = (GSList **)data;
677         if (header->ftype == FTYPE_CATEGORY) {
678                 *list = g_slist_prepend (*list, header);
679         }
680         return TRUE;
681 }
682
683 static GSList*
684 get_custom_categories (void) {
685         GSList *list = NULL;
686         foreach_shared_item (category_collect, &list);
687         return list;
688 }
689
690 static char*
691 custom_category_counters (SharedCategory* cat)
692 {
693         char *p = cat->name + strlen (cat->name) + 1;
694         p += strlen (p) + 1; /* skip category help */
695         return p;
696 }
697
698 static SharedCounter*
699 find_custom_counter (SharedCategory* cat, MonoString *name)
700 {
701         int i;
702         char *p = custom_category_counters (cat);
703         for (i = 0; i < cat->num_counters; ++i) {
704                 SharedCounter *counter = (SharedCounter*)p;
705                 if (mono_string_compare_ascii (name, counter->name) == 0)
706                         return counter;
707                 p += 2; /* skip counter type */
708                 p += strlen (p) + 1; /* skip counter name */
709                 p += strlen (p) + 1; /* skip counter help */
710         }
711         return NULL;
712 }
713
714 typedef struct {
715         unsigned int cat_offset;
716         SharedCategory* cat;
717         char *name;
718         SharedInstance* result;
719         GSList *list;
720 } InstanceSearch;
721
722 static gboolean
723 instance_search (SharedHeader *header, void *data)
724 {
725         InstanceSearch *search = (InstanceSearch *)data;
726         if (header->ftype == FTYPE_INSTANCE) {
727                 SharedInstance *ins = (SharedInstance*)header;
728                 if (search->cat_offset == ins->category_offset) {
729                         if (search->name) {
730                                 if (strcmp (search->name, ins->instance_name) == 0) {
731                                         search->result = ins;
732                                         return FALSE;
733                                 }
734                         } else {
735                                 search->list = g_slist_prepend (search->list, ins);
736                         }
737                 }
738         }
739         return TRUE;
740 }
741
742 static SharedInstance*
743 find_custom_instance (SharedCategory* cat, char *name)
744 {
745         InstanceSearch search;
746         search.cat_offset = (char*)cat - (char*)shared_area;
747         search.cat = cat;
748         search.name = name;
749         search.list = NULL;
750         search.result = NULL;
751         foreach_shared_item (instance_search, &search);
752         return search.result;
753 }
754
755 static GSList*
756 get_custom_instances_list (SharedCategory* cat)
757 {
758         InstanceSearch search;
759         search.cat_offset = (char*)cat - (char*)shared_area;
760         search.cat = cat;
761         search.name = NULL;
762         search.list = NULL;
763         search.result = NULL;
764         foreach_shared_item (instance_search, &search);
765         return search.list;
766 }
767
768 static char*
769 custom_category_help (SharedCategory* cat)
770 {
771         return cat->name + strlen (cat->name) + 1;
772 }
773
774 static const CounterDesc*
775 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
776 {
777         const CounterDesc *cdesc = &predef_counters [desc->first_counter];
778         const CounterDesc *end = &predef_counters [desc [1].first_counter];
779         for (; cdesc < end; ++cdesc) {
780                 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
781                         return cdesc;
782         }
783         return NULL;
784 }
785
786 /* fill the info in sample (except the raw value) */
787 static void
788 fill_sample (MonoCounterSample *sample)
789 {
790         sample->timeStamp = mono_100ns_ticks ();
791         sample->timeStamp100nSec = sample->timeStamp;
792         sample->counterTimeStamp = sample->timeStamp;
793         sample->counterFrequency = 10000000;
794         sample->systemFrequency = 10000000;
795         // the real basevalue needs to be get from a different counter...
796         sample->baseValue = 0;
797 }
798
799 static int
800 id_from_string (const gchar *id_str, gboolean is_process)
801 {
802         int id = -1;
803         if (strcmp("", id_str) != 0) {
804                 char *end;
805                 id = strtol (id_str, &end, 0);
806                 if (end == id_str && !is_process)
807                         id = -1;
808         }
809         return id;
810 }
811
812 static MonoBoolean
813 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
814 {
815         MonoProcessError error;
816         int id = GPOINTER_TO_INT (vtable->arg);
817         int pid = id >> 5;
818         id &= 0x1f;
819         if (!only_value) {
820                 fill_sample (sample);
821                 sample->baseValue = 1;
822         }
823         sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
824         switch (id) {
825         case COUNTER_CPU_USER_TIME:
826                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
827                 return TRUE;
828         case COUNTER_CPU_PRIV_TIME:
829                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
830                 return TRUE;
831         case COUNTER_CPU_INTR_TIME:
832                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
833                 return TRUE;
834         case COUNTER_CPU_DCP_TIME:
835                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
836                 return TRUE;
837         case COUNTER_CPU_PROC_TIME:
838                 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
839                 return TRUE;
840         }
841         return FALSE;
842 }
843
844 static void*
845 cpu_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom)
846 {
847         int id = id_from_string (instance, FALSE) << 5;
848         const CounterDesc *cdesc;
849         *custom = FALSE;
850         /* increase the shift above and the mask also in the implementation functions */
851         //g_assert (32 > desc [1].first_counter - desc->first_counter);
852         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
853                 *type = cdesc->type;
854                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
855         }
856         return NULL;
857 }
858
859 static MonoBoolean
860 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
861 {
862         MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
863         NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
864         if (!only_value) {
865                 fill_sample (sample);
866         }
867
868         sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
869         switch (narg->id) {
870         case COUNTER_NETWORK_BYTESRECSEC:
871                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
872                 break;
873         case COUNTER_NETWORK_BYTESSENTSEC:
874                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
875                 break;
876         case COUNTER_NETWORK_BYTESTOTALSEC:
877                 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
878                 break;
879         }
880
881         if (error == MONO_NETWORK_ERROR_NONE)
882                 return TRUE;
883         else
884                 return FALSE;
885 }
886
887 static void
888 network_cleanup (ImplVtable *vtable)
889 {
890         NetworkVtableArg *narg;
891
892         if (vtable == NULL)
893                 return;
894
895         narg = (NetworkVtableArg *)vtable->arg;
896         if (narg == NULL)
897                 return;
898
899         g_free (narg->name);
900         narg->name = NULL;
901         g_free (narg);
902         vtable->arg = NULL;
903 }
904
905 static void*
906 network_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom)
907 {
908         const CounterDesc *cdesc;
909         NetworkVtableArg *narg;
910         ImplVtable *vtable;
911         char *instance_name;
912
913         *custom = FALSE;
914         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
915                 instance_name = g_strdup (instance);
916                 narg = g_new0 (NetworkVtableArg, 1);
917                 narg->id = cdesc->id;
918                 narg->name = instance_name;
919                 *type = cdesc->type;
920                 vtable = create_vtable (narg, get_network_counter, NULL);
921                 vtable->cleanup = network_cleanup;
922                 return vtable;
923         }
924         return NULL;
925 }
926
927 static MonoBoolean
928 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
929 {
930         int id = GPOINTER_TO_INT (vtable->arg);
931         int pid = id >> 5;
932         if (pid < 0)
933                 return FALSE;
934         id &= 0x1f;
935         if (!only_value) {
936                 fill_sample (sample);
937                 sample->baseValue = 1;
938         }
939         sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
940         switch (id) {
941         case COUNTER_PROC_USER_TIME:
942                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
943                 return TRUE;
944         case COUNTER_PROC_PRIV_TIME:
945                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
946                 return TRUE;
947         case COUNTER_PROC_PROC_TIME:
948                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
949                 return TRUE;
950         case COUNTER_PROC_THREADS:
951                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
952                 return TRUE;
953         case COUNTER_PROC_VBYTES:
954                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
955                 return TRUE;
956         case COUNTER_PROC_WSET:
957                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
958                 return TRUE;
959         case COUNTER_PROC_PBYTES:
960                 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
961                 return TRUE;
962         }
963         return FALSE;
964 }
965
966 static void*
967 process_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom)
968 {
969         int id = id_from_string (instance, TRUE) << 5;
970         const CounterDesc *cdesc;
971         *custom = FALSE;
972         /* increase the shift above and the mask also in the implementation functions */
973         //g_assert (32 > desc [1].first_counter - desc->first_counter);
974         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
975                 *type = cdesc->type;
976                 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
977         }
978         return NULL;
979 }
980
981 static MonoBoolean
982 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
983 {
984         int id = GPOINTER_TO_INT (vtable->arg);
985         if (!only_value) {
986                 fill_sample (sample);
987                 sample->baseValue = 1;
988         }
989         sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
990         switch (id) {
991         case COUNTER_MEM_NUM_OBJECTS:
992                 sample->rawValue = 0;
993                 return TRUE;
994         case COUNTER_MEM_PHYS_TOTAL:
995                 sample->rawValue = mono_determine_physical_ram_size ();;
996                 return TRUE;
997         case COUNTER_MEM_PHYS_AVAILABLE:
998                 sample->rawValue = mono_determine_physical_ram_available_size ();;
999                 return TRUE;
1000         }
1001         return FALSE;
1002 }
1003
1004 static void*
1005 mono_mem_get_impl (MonoString* counter, const gchar* instance, int *type, MonoBoolean *custom)
1006 {
1007         const CounterDesc *cdesc;
1008         *custom = FALSE;
1009         if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1010                 *type = cdesc->type;
1011                 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1012         }
1013         return NULL;
1014 }
1015
1016 static MonoBoolean
1017 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1018 {
1019         PredefVtable *vt = (PredefVtable *)vtable;
1020         const CounterDesc *desc;
1021         int cat_id = GPOINTER_TO_INT (vtable->arg);
1022         int id = cat_id >> 16;
1023         cat_id &= 0xffff;
1024         if (!only_value) {
1025                 fill_sample (sample);
1026                 sample->baseValue = 1;
1027         }
1028         desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1029         sample->counterType = desc->type;
1030         /* FIXME: check that the offset fits inside imported counters */
1031         /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1032         sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1033         return TRUE;
1034 }
1035
1036 static ImplVtable*
1037 predef_vtable (void *arg, const gchar *pids)
1038 {
1039         MonoSharedArea *area;
1040         PredefVtable *vtable;
1041         int pid;
1042
1043         pid = atoi (pids);
1044         area = load_sarea_for_pid (pid);
1045         if (!area)
1046                 return NULL;
1047
1048         vtable = g_new (PredefVtable, 1);
1049         vtable->vtable.arg = arg;
1050         vtable->vtable.sample = predef_readonly_counter;
1051         vtable->vtable.cleanup = predef_cleanup;
1052         vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1053         vtable->pid = pid;
1054
1055         return (ImplVtable*)vtable;
1056 }
1057
1058 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1059  * this needs some way to set sample->counterType as well, though.
1060  */
1061 static MonoBoolean
1062 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1063 {
1064         int cat_id = GPOINTER_TO_INT (vtable->arg);
1065         int id = cat_id >> 16;
1066         cat_id &= 0xffff;
1067         if (!only_value) {
1068                 fill_sample (sample);
1069                 sample->baseValue = 1;
1070         }
1071         sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1072         switch (cat_id) {
1073         case CATEGORY_EXC:
1074                 switch (id) {
1075                 case COUNTER_EXC_THROWN:
1076                         sample->rawValue = mono_perfcounters->exceptions_thrown;
1077                         return TRUE;
1078                 }
1079                 break;
1080         case CATEGORY_ASPNET:
1081                 switch (id) {
1082                 case COUNTER_ASPNET_REQ_Q:
1083                         sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1084                         return TRUE;
1085                 case COUNTER_ASPNET_REQ_TOTAL:
1086                         sample->rawValue = mono_perfcounters->aspnet_requests;
1087                         return TRUE;
1088                 }
1089                 break;
1090         case CATEGORY_THREADPOOL:
1091                 switch (id) {
1092                 case COUNTER_THREADPOOL_WORKITEMS:
1093                         sample->rawValue = mono_perfcounters->threadpool_workitems;
1094                         return TRUE;
1095                 case COUNTER_THREADPOOL_IOWORKITEMS:
1096                         sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1097                         return TRUE;
1098                 case COUNTER_THREADPOOL_THREADS:
1099                         sample->rawValue = mono_perfcounters->threadpool_threads;
1100                         return TRUE;
1101                 case COUNTER_THREADPOOL_IOTHREADS:
1102                         sample->rawValue = mono_perfcounters->threadpool_iothreads;
1103                         return TRUE;
1104                 }
1105                 break;
1106         case CATEGORY_JIT:
1107                 switch (id) {
1108                 case COUNTER_JIT_BYTES:
1109                         sample->rawValue = mono_perfcounters->jit_bytes;
1110                         return TRUE;
1111                 case COUNTER_JIT_METHODS:
1112                         sample->rawValue = mono_perfcounters->jit_methods;
1113                         return TRUE;
1114                 case COUNTER_JIT_TIME:
1115                         sample->rawValue = mono_perfcounters->jit_time;
1116                         return TRUE;
1117                 case COUNTER_JIT_BYTES_PSEC:
1118                         sample->rawValue = mono_perfcounters->jit_bytes;
1119                         return TRUE;
1120                 case COUNTER_JIT_FAILURES:
1121                         sample->rawValue = mono_perfcounters->jit_failures;
1122                         return TRUE;
1123                 }
1124                 break;
1125         }
1126         return FALSE;
1127 }
1128
1129 static gint64
1130 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1131 {
1132         guint32 *volatile ptr = NULL;
1133         gint64 *volatile ptr64 = NULL;
1134         int cat_id = GPOINTER_TO_INT (vtable->arg);
1135         int id = cat_id >> 16;
1136         cat_id &= 0xffff;
1137         switch (cat_id) {
1138         case CATEGORY_ASPNET:
1139                 switch (id) {
1140                 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1141                 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1142                 }
1143                 break;
1144         case CATEGORY_THREADPOOL:
1145                 switch (id) {
1146                 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1147                 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1148                 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1149                 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1150                 }
1151                 break;
1152         }
1153         if (ptr) {
1154                 if (do_incr) {
1155                         if (value == 1)
1156                                 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1157                         if (value == -1)
1158                                 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1159
1160                         *ptr += value;
1161                         return *ptr;
1162                 }
1163                 /* this can be non-atomic */
1164                 *ptr = value;
1165                 return value;
1166         } else if (ptr64) {
1167                 if (do_incr) {
1168                         /* FIXME: we need to do this atomically */
1169                         /* No InterlockedIncrement64() yet */
1170                         /*
1171                         if (value == 1)
1172                                 return InterlockedIncrement64 (ptr);
1173                         if (value == -1)
1174                                 return InterlockedDecrement64 (ptr);
1175                         */
1176
1177                         *ptr64 += value;
1178                         return *ptr64;
1179                 }
1180                 /* this can be non-atomic */
1181                 *ptr64 = value;
1182                 return value;
1183         }
1184         return 0;
1185 }
1186
1187 static void*
1188 predef_writable_get_impl (int cat, MonoString* counter, const gchar *instance, int *type, MonoBoolean *custom)
1189 {
1190         const CounterDesc *cdesc;
1191         *custom = TRUE;
1192         if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1193                 *type = cdesc->type;
1194                 if (instance == NULL || strcmp (instance, "") == 0)
1195                         return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1196                 else
1197                         return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1198         }
1199         return NULL;
1200 }
1201
1202 static MonoBoolean
1203 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1204 {
1205         CustomVTable *counter_data = (CustomVTable *)vtable;
1206         if (!only_value) {
1207                 fill_sample (sample);
1208                 sample->baseValue = 1;
1209         }
1210         sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1211         if (!vtable->arg)
1212                 sample->rawValue = 0;
1213         else
1214                 sample->rawValue = *(guint64*)vtable->arg;
1215         return TRUE;
1216 }
1217
1218 static gint64
1219 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1220 {
1221         /* FIXME: check writability */
1222         guint64 *ptr = (guint64 *)vtable->arg;
1223         if (ptr) {
1224                 if (do_incr) {
1225                         /* FIXME: we need to do this atomically */
1226                         *ptr += value;
1227                         return *ptr;
1228                 }
1229                 /* this can be non-atomic */
1230                 *ptr = value;
1231                 return value;
1232         }
1233         return 0;
1234 }
1235
1236 static SharedInstance*
1237 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1238 {
1239         SharedInstance* inst;
1240         char *p;
1241         int size;
1242         inst = find_custom_instance (cat, name);
1243         if (inst)
1244                 return inst;
1245         size = sizeof (SharedInstance) + strlen (name);
1246         size += 7;
1247         size &= ~7;
1248         size += (sizeof (guint64) * cat->num_counters);
1249         perfctr_lock ();
1250         inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1251         if (!inst) {
1252                 perfctr_unlock ();
1253                 g_free (name);
1254                 return NULL;
1255         }
1256         inst->category_offset = (char*)cat - (char*)shared_area;
1257         cat->num_instances++;
1258         /* now copy the variable data */
1259         p = inst->instance_name;
1260         strcpy (p, name);
1261         p += strlen (name) + 1;
1262         perfctr_unlock ();
1263
1264         return inst;
1265 }
1266
1267 static ImplVtable*
1268 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1269 {
1270         CustomVTable* vtable;
1271         vtable = g_new0 (CustomVTable, 1);
1272         vtable->vtable.arg = data;
1273         vtable->vtable.sample = custom_writable_counter;
1274         vtable->vtable.update = custom_writable_update;
1275         vtable->instance_desc = inst;
1276         vtable->counter_desc = scounter;
1277
1278         return (ImplVtable*)vtable;
1279 }
1280
1281 static gpointer
1282 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1283 {
1284         int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1285         offset += 7;
1286         offset &= ~7;
1287         offset += scounter->seq_num * sizeof (guint64);
1288         return (char*)sinst + offset;
1289 }
1290
1291 static void*
1292 custom_get_impl (SharedCategory *cat, MonoString *counter, MonoString* instance, int *type, MonoError *error)
1293 {
1294         SharedCounter *scounter;
1295         SharedInstance* inst;
1296         char *name;
1297
1298         error_init (error);
1299         scounter = find_custom_counter (cat, counter);
1300         if (!scounter)
1301                 return NULL;
1302         name = mono_string_to_utf8_checked (counter, error);
1303         return_val_if_nok (error, NULL);
1304         *type = simple_type_to_type [scounter->type];
1305         inst = custom_get_instance (cat, scounter, name);
1306         g_free (name);
1307         if (!inst)
1308                 return NULL;
1309         return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1310 }
1311
1312 static const CategoryDesc*
1313 find_category (MonoString *category)
1314 {
1315         int i;
1316         for (i = 0; i < NUM_CATEGORIES; ++i) {
1317                 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1318                         return &predef_categories [i];
1319         }
1320         return NULL;
1321 }
1322
1323 void*
1324 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1325                 MonoString* machine, int *type, MonoBoolean *custom)
1326 {
1327         MonoError error;
1328         const CategoryDesc *cdesc;
1329         void *result = NULL;
1330         /* no support for counters on other machines */
1331         if (mono_string_compare_ascii (machine, "."))
1332                 return NULL;
1333         cdesc = find_category (category);
1334         if (!cdesc) {
1335                 SharedCategory *scat = find_custom_category (category);
1336                 if (!scat)
1337                         return NULL;
1338                 *custom = TRUE;
1339                 result = custom_get_impl (scat, counter, instance, type, &error);
1340                 if (mono_error_set_pending_exception (&error))
1341                         return NULL;
1342                 return result;
1343         }
1344         gchar *c_instance = mono_string_to_utf8_checked (instance, &error);
1345         if (mono_error_set_pending_exception (&error))
1346                 return NULL;
1347         switch (cdesc->id) {
1348         case CATEGORY_CPU:
1349                 result = cpu_get_impl (counter, c_instance, type, custom);
1350                 break;
1351         case CATEGORY_PROC:
1352                 result = process_get_impl (counter, c_instance, type, custom);
1353                 break;
1354         case CATEGORY_MONO_MEM:
1355                 result = mono_mem_get_impl (counter, c_instance, type, custom);
1356                 break;
1357         case CATEGORY_NETWORK:
1358                 result = network_get_impl (counter, c_instance, type, custom);
1359                 break;
1360         case CATEGORY_JIT:
1361         case CATEGORY_EXC:
1362         case CATEGORY_GC:
1363         case CATEGORY_REMOTING:
1364         case CATEGORY_LOADING:
1365         case CATEGORY_THREAD:
1366         case CATEGORY_INTEROP:
1367         case CATEGORY_SECURITY:
1368         case CATEGORY_ASPNET:
1369         case CATEGORY_THREADPOOL:
1370                 result = predef_writable_get_impl (cdesc->id, counter, c_instance, type, custom);
1371                 break;
1372         }
1373         g_free (c_instance);
1374         return result;
1375 }
1376
1377 MonoBoolean
1378 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1379 {
1380         ImplVtable *vtable = (ImplVtable *)impl;
1381         if (vtable && vtable->sample)
1382                 return vtable->sample (vtable, only_value, sample);
1383         return FALSE;
1384 }
1385
1386 gint64
1387 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1388 {
1389         ImplVtable *vtable = (ImplVtable *)impl;
1390         if (vtable && vtable->update)
1391                 return vtable->update (vtable, do_incr, value);
1392         return 0;
1393 }
1394
1395 void
1396 mono_perfcounter_free_data (void *impl)
1397 {
1398         ImplVtable *vtable = (ImplVtable *)impl;
1399         if (vtable && vtable->cleanup)
1400                 vtable->cleanup (vtable);
1401         g_free (impl);
1402 }
1403
1404 /* Category icalls */
1405 MonoBoolean
1406 mono_perfcounter_category_del (MonoString *name)
1407 {
1408         const CategoryDesc *cdesc;
1409         SharedCategory *cat;
1410         cdesc = find_category (name);
1411         /* can't delete a predefined category */
1412         if (cdesc)
1413                 return FALSE;
1414         perfctr_lock ();
1415         cat = find_custom_category (name);
1416         /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1417         if (!cat || cat->num_instances) {
1418                 perfctr_unlock ();
1419                 return FALSE;
1420         }
1421         cat->header.ftype = FTYPE_DELETED;
1422         perfctr_unlock ();
1423         return TRUE;
1424 }
1425
1426 /* this is an icall */
1427 MonoString*
1428 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1429 {
1430         MonoError error;
1431         MonoString *result = NULL;
1432         const CategoryDesc *cdesc;
1433         error_init (&error);
1434         /* no support for counters on other machines */
1435         if (mono_string_compare_ascii (machine, "."))
1436                 return NULL;
1437         cdesc = find_category (category);
1438         if (!cdesc) {
1439                 SharedCategory *scat = find_custom_category (category);
1440                 if (!scat)
1441                         return NULL;
1442                 result = mono_string_new_checked (mono_domain_get (), custom_category_help (scat), &error);
1443                 if (mono_error_set_pending_exception (&error))
1444                         return NULL;
1445                 return result;
1446         }
1447         result = mono_string_new_checked (mono_domain_get (), cdesc->help, &error);
1448         if (mono_error_set_pending_exception (&error))
1449                 return NULL;
1450         return result;
1451 }
1452
1453 /*
1454  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1455  * TRUE only if a counter with that name exists in the category.
1456  */
1457 MonoBoolean
1458 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1459 {
1460         const CategoryDesc *cdesc;
1461         /* no support for counters on other machines */
1462         if (mono_string_compare_ascii (machine, "."))
1463                 return FALSE;
1464         cdesc = find_category (category);
1465         if (!cdesc) {
1466                 SharedCategory *scat = find_custom_category (category);
1467                 if (!scat)
1468                         return FALSE;
1469                 /* counter is allowed to be null */
1470                 if (!counter)
1471                         return TRUE;
1472                 /* search through the custom category */
1473                 return find_custom_counter (scat, counter) != NULL;
1474         }
1475         /* counter is allowed to be null */
1476         if (!counter)
1477                 return TRUE;
1478         if (get_counter_in_category (cdesc, counter))
1479                 return TRUE;
1480         return FALSE;
1481 }
1482
1483 /* C map of the type with the same name */
1484 typedef struct {
1485         MonoObject object;
1486         MonoString *help;
1487         MonoString *name;
1488         int type;
1489 } CounterCreationData;
1490
1491 /*
1492  * Since we'll keep a copy of the category per-process, we should also make sure
1493  * categories with the same name are compatible.
1494  */
1495 MonoBoolean
1496 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1497 {
1498         MonoError error;
1499         int result = FALSE;
1500         int i, size;
1501         int num_counters = mono_array_length (items);
1502         int counters_data_size;
1503         char *name = NULL;
1504         char *chelp = NULL;
1505         char **counter_info = NULL;
1506         char *p;
1507         SharedCategory *cat;
1508
1509         /* FIXME: ensure there isn't a category created already */
1510         name = mono_string_to_utf8_checked (category, &error);
1511         if (!mono_error_ok (&error))
1512                 goto failure;
1513         chelp = mono_string_to_utf8_checked (help, &error);
1514         if (!mono_error_ok (&error))
1515                 goto failure;
1516         counter_info = g_new0 (char*, num_counters * 2);
1517         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1518         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1519         for (i = 0; i < num_counters; ++i) {
1520                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1521                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1522                 if (!mono_error_ok (&error))
1523                         goto failure;
1524                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1525                 if (!mono_error_ok (&error))
1526                         goto failure;
1527                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1528         }
1529         for (i = 0; i < num_counters * 2; ++i) {
1530                 if (!counter_info [i])
1531                         goto failure;
1532                 size += strlen (counter_info [i]) + 1;
1533         }
1534         size += 7;
1535         size &= ~7;
1536         counters_data_size = num_counters * 8; /* optimize for size later */
1537         if (size > 65535)
1538                 goto failure;
1539         perfctr_lock ();
1540         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1541         if (!cat) {
1542                 perfctr_unlock ();
1543                 goto failure;
1544         }
1545         cat->num_counters = num_counters;
1546         cat->counters_data_size = counters_data_size;
1547         /* now copy the vaiable data */
1548         p = cat->name;
1549         strcpy (p, name);
1550         p += strlen (name) + 1;
1551         strcpy (p, chelp);
1552         p += strlen (chelp) + 1;
1553         for (i = 0; i < num_counters; ++i) {
1554                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1555                 /* emit the SharedCounter structures */
1556                 *p++ = perfctr_type_compress (data->type);
1557                 *p++ = i;
1558                 strcpy (p, counter_info [i * 2]);
1559                 p += strlen (counter_info [i * 2]) + 1;
1560                 strcpy (p, counter_info [i * 2 + 1]);
1561                 p += strlen (counter_info [i * 2 + 1]) + 1;
1562         }
1563
1564         perfctr_unlock ();
1565         result = TRUE;
1566 failure:
1567         if (counter_info) {
1568                 for (i = 0; i < num_counters * 2; ++i) {
1569                         g_free (counter_info [i]);
1570                 }
1571                 g_free (counter_info);
1572         }
1573         g_free (name);
1574         g_free (chelp);
1575         mono_error_cleanup (&error);
1576         return result;
1577 }
1578
1579 int
1580 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1581 {
1582         MonoError error;
1583         const CategoryDesc *cdesc;
1584         SharedInstance *sinst;
1585         char *name;
1586         /* no support for counters on other machines */
1587         /*FIXME: machine appears to be wrong
1588         if (mono_string_compare_ascii (machine, "."))
1589                 return FALSE;*/
1590         cdesc = find_category (category);
1591         if (!cdesc) {
1592                 SharedCategory *scat;
1593                 scat = find_custom_category (category);
1594                 if (!scat)
1595                         return FALSE;
1596                 name = mono_string_to_utf8_checked (instance, &error);
1597                 if (mono_error_set_pending_exception (&error))
1598                         return FALSE;
1599                 sinst = find_custom_instance (scat, name);
1600                 g_free (name);
1601                 if (sinst)
1602                         return TRUE;
1603         } else {
1604                 /* FIXME: search instance */
1605         }
1606         return FALSE;
1607 }
1608
1609 /* this is an icall */
1610 MonoArray*
1611 mono_perfcounter_category_names (MonoString *machine)
1612 {
1613         MonoError error;
1614         int i;
1615         MonoArray *res;
1616         MonoDomain *domain = mono_domain_get ();
1617         GSList *custom_categories, *tmp;
1618         /* no support for counters on other machines */
1619         if (mono_string_compare_ascii (machine, ".")) {
1620                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1621                 mono_error_set_pending_exception (&error);
1622                 return res;
1623         }
1624         perfctr_lock ();
1625         custom_categories = get_custom_categories ();
1626         res = mono_array_new_checked (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories), &error);
1627         if (mono_error_set_pending_exception (&error)) {
1628                 perfctr_unlock ();
1629                 return NULL;
1630         }
1631
1632         for (i = 0; i < NUM_CATEGORIES; ++i) {
1633                 const CategoryDesc *cdesc = &predef_categories [i];
1634                 MonoString *name = mono_string_new_checked (domain, cdesc->name, &error);
1635                 if (!is_ok (&error))
1636                         goto leave;
1637                 mono_array_setref (res, i, name);
1638         }
1639         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1640                 SharedCategory *scat = (SharedCategory *)tmp->data;
1641                 MonoString *name = mono_string_new_checked (domain, scat->name, &error);
1642                 if (!is_ok (&error))
1643                         goto leave;
1644                 mono_array_setref (res, i, name);
1645                 i++;
1646         }
1647 leave:
1648         perfctr_unlock ();
1649         g_slist_free (custom_categories);
1650         mono_error_set_pending_exception (&error);
1651         return res;
1652 }
1653
1654 MonoArray*
1655 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1656 {
1657         MonoError error;
1658         int i;
1659         SharedCategory *scat;
1660         const CategoryDesc *cdesc;
1661         MonoArray *res;
1662         MonoDomain *domain = mono_domain_get ();
1663         /* no support for counters on other machines */
1664         if (mono_string_compare_ascii (machine, ".")) {
1665                 res =  mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1666                 mono_error_set_pending_exception (&error);
1667                 return res;
1668         }
1669         cdesc = find_category (category);
1670         if (cdesc) {
1671                 res = mono_array_new_checked (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter, &error);
1672                 if (mono_error_set_pending_exception (&error))
1673                         return NULL;
1674                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1675                         const CounterDesc *desc = &predef_counters [i];
1676                         MonoString *name = mono_string_new_checked (domain, desc->name, &error);
1677                         if (mono_error_set_pending_exception (&error))
1678                                 return NULL;
1679                         mono_array_setref (res, i - cdesc->first_counter, name);
1680                 }
1681                 return res;
1682         }
1683         perfctr_lock ();
1684         scat = find_custom_category (category);
1685         if (scat) {
1686                 char *p = custom_category_counters (scat);
1687                 int i;
1688                 res = mono_array_new_checked (domain, mono_get_string_class (), scat->num_counters, &error);
1689                 if (mono_error_set_pending_exception (&error)) {
1690                         perfctr_unlock ();
1691                         return NULL;
1692                 }
1693
1694                 for (i = 0; i < scat->num_counters; ++i) {
1695                         MonoString *str = mono_string_new_checked (domain, p + 1, &error);
1696                         if (!is_ok (&error))
1697                                 goto leave;
1698                         mono_array_setref (res, i, str);
1699                         p += 2; /* skip counter type */
1700                         p += strlen (p) + 1; /* skip counter name */
1701                         p += strlen (p) + 1; /* skip counter help */
1702                 }
1703         } else
1704                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1705 leave:
1706         perfctr_unlock ();
1707         mono_error_set_pending_exception (&error);
1708         return res;
1709 }
1710
1711 static MonoArray*
1712 get_string_array (void **array, int count, gboolean is_process, MonoError *error)
1713 {
1714         int i;
1715         MonoDomain *domain = mono_domain_get ();
1716         error_init (error);
1717         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1718         return_val_if_nok (error, NULL);
1719         for (i = 0; i < count; ++i) {
1720                 char buf [128];
1721                 char *p;
1722                 if (is_process) {
1723                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1724                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1725                 } else {
1726                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1727                         p = buf;
1728                 }
1729                 MonoString *str = mono_string_new_checked (domain, p, error);
1730                 if (p != buf)
1731                         g_free (p);
1732                 return_val_if_nok (error, NULL);
1733                 mono_array_setref (res, i, str);
1734         }
1735         return res;
1736 }
1737
1738 static MonoArray*
1739 get_string_array_of_strings (void **array, int count, MonoError *error)
1740 {
1741         int i;
1742         MonoDomain *domain = mono_domain_get ();
1743         error_init (error);
1744         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1745         return_val_if_nok (error, NULL);
1746         for (i = 0; i < count; ++i) {
1747                 char* p = (char *)array[i];
1748                 MonoString *str = mono_string_new_checked (domain, p, error);
1749                 return_val_if_nok (error, NULL);
1750                 mono_array_setref (res, i, str);
1751         }
1752
1753         return res;
1754 }
1755
1756 static MonoArray*
1757 get_mono_instances (MonoError *error)
1758 {
1759         int count = 64;
1760         int res;
1761         void **buf = NULL;
1762         MonoArray *array;
1763         error_init (error);
1764         do {
1765                 count *= 2;
1766                 g_free (buf);
1767                 buf = g_new (void*, count);
1768                 res = mono_shared_area_instances (buf, count);
1769         } while (res == count);
1770         array = get_string_array (buf, res, TRUE, error);
1771         g_free (buf);
1772         return array;
1773 }
1774
1775 static MonoArray*
1776 get_cpu_instances (MonoError *error)
1777 {
1778         void **buf = NULL;
1779         int i, count;
1780         MonoArray *array;
1781         error_init (error);
1782         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1783         buf = g_new (void*, count);
1784         for (i = 0; i < count; ++i)
1785                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1786         array = get_string_array (buf, count, FALSE, error);
1787         g_free (buf);
1788         MonoString *total = mono_string_new_checked (mono_domain_get (), "_Total", error);
1789         return_val_if_nok (error, NULL);
1790         mono_array_setref (array, 0, total);
1791         return array;
1792 }
1793
1794 static MonoArray*
1795 get_processes_instances (MonoError *error)
1796 {
1797         MonoArray *array;
1798         int count = 0;
1799         void **buf = mono_process_list (&count);
1800         error_init (error);
1801         if (!buf)
1802                 return get_string_array (NULL, 0, FALSE, error);
1803         array = get_string_array (buf, count, TRUE, error);
1804         g_free (buf);
1805         return array;
1806 }
1807
1808 static MonoArray*
1809 get_networkinterface_instances (MonoError *error)
1810 {
1811         MonoArray *array;
1812         int count = 0;
1813         error_init (error);
1814         void **buf = mono_networkinterface_list (&count);
1815         if (!buf)
1816                 return get_string_array_of_strings (NULL, 0, error);
1817         array = get_string_array_of_strings (buf, count, error);
1818         g_strfreev ((char **) buf);
1819         return array;
1820 }
1821
1822 static MonoArray*
1823 get_custom_instances (MonoString *category, MonoError *error)
1824 {
1825         SharedCategory *scat;
1826         error_init (error);
1827         scat = find_custom_category (category);
1828         if (scat) {
1829                 GSList *list = get_custom_instances_list (scat);
1830                 GSList *tmp;
1831                 int i = 0;
1832                 MonoArray *array = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), g_slist_length (list), error);
1833                 if (!is_ok (error)) {
1834                         g_slist_free (list);
1835                         return NULL;
1836                 }
1837                 for (tmp = list; tmp; tmp = tmp->next) {
1838                         SharedInstance *inst = (SharedInstance *)tmp->data;
1839                         MonoString *str = mono_string_new_checked (mono_domain_get (), inst->instance_name, error);
1840                         if (!is_ok (error)) {
1841                                 g_slist_free (list);
1842                                 return NULL;
1843                         }
1844                         mono_array_setref (array, i, str);
1845                         i++;
1846                 }
1847                 g_slist_free (list);
1848                 return array;
1849         }
1850         return mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, error);
1851 }
1852
1853 MonoArray*
1854 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1855 {
1856         MonoError error;
1857         const CategoryDesc* cat;
1858         MonoArray *result = NULL;
1859         if (mono_string_compare_ascii (machine, ".")) {
1860                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1861                 mono_error_set_pending_exception (&error);
1862                 return result;
1863         }
1864         
1865         cat = find_category (category);
1866         if (!cat) {
1867                 MonoArray *result = get_custom_instances (category, &error);
1868                 mono_error_set_pending_exception (&error);
1869                 return result;
1870         }
1871         switch (cat->instance_type) {
1872         case MonoInstance:
1873                 result = get_mono_instances (&error);
1874                 break;
1875         case CPUInstance:
1876                 result = get_cpu_instances (&error);
1877                 break;
1878         case ProcessInstance:
1879                 result = get_processes_instances (&error);
1880                 break;
1881         case NetworkInterfaceInstance:
1882                 result = get_networkinterface_instances (&error);
1883                 break;
1884         case ThreadInstance:
1885         default:
1886                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1887         }
1888         mono_error_set_pending_exception (&error);
1889         return result;
1890 }
1891
1892 typedef struct {
1893         PerfCounterEnumCallback cb;
1894         void *data;
1895 } PerfCounterForeachData;
1896
1897 static gboolean
1898 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1899 {
1900         int i;
1901         char *p, *name;
1902         unsigned char type;
1903         void *addr;
1904         SharedCategory *cat;
1905         SharedCounter *counter;
1906         SharedInstance *inst;
1907         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1908
1909         if (header->ftype == FTYPE_CATEGORY) {
1910                 cat = (SharedCategory*)header;
1911
1912                 p = cat->name;
1913                 p += strlen (p) + 1; /* skip category name */
1914                 p += strlen (p) + 1; /* skip category help */
1915
1916                 for (i = 0; i < cat->num_counters; ++i) {
1917                         counter = (SharedCounter*) p;
1918                         type = (unsigned char)*p++;
1919                         /* seq_num = (int)* */ p++;
1920                         name = p;
1921                         p += strlen (p) + 1;
1922                         /* help = p; */
1923                         p += strlen (p) + 1;
1924
1925                         inst = custom_get_instance (cat, counter, name);
1926                         if (!inst)
1927                                 return FALSE;
1928                         addr = custom_get_value_address (counter, inst);
1929                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1930                                 return FALSE;
1931                 }
1932         }
1933
1934         return TRUE;
1935 }
1936
1937 void
1938 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1939 {
1940         PerfCounterForeachData foreach_data = { cb, data };
1941
1942         perfctr_lock ();
1943
1944         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1945
1946         perfctr_unlock ();
1947 }
1948
1949 #else
1950 void*
1951 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1952 {
1953         g_assert_not_reached ();
1954 }
1955
1956 MonoBoolean
1957 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1958 {
1959         g_assert_not_reached ();
1960 }
1961
1962 gint64
1963 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1964 {
1965         g_assert_not_reached ();
1966 }
1967
1968 void
1969 mono_perfcounter_free_data (void *impl)
1970 {
1971         g_assert_not_reached ();
1972 }
1973
1974 /* Category icalls */
1975 MonoBoolean
1976 mono_perfcounter_category_del (MonoString *name)
1977 {
1978         g_assert_not_reached ();
1979 }
1980
1981 MonoString*
1982 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1983 {
1984         g_assert_not_reached ();
1985 }
1986
1987 MonoBoolean
1988 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1989 {
1990         g_assert_not_reached ();
1991 }
1992
1993 MonoBoolean
1994 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1995 {
1996         g_assert_not_reached ();
1997 }
1998
1999 int
2000 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
2001 {
2002         g_assert_not_reached ();
2003 }
2004
2005 MonoArray*
2006 mono_perfcounter_category_names (MonoString *machine)
2007 {
2008         g_assert_not_reached ();
2009 }
2010
2011 MonoArray*
2012 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
2013 {
2014         g_assert_not_reached ();
2015 }
2016
2017 MonoArray*
2018 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
2019 {
2020         g_assert_not_reached ();
2021 }
2022 #endif