[io-layer] Extract error (#4279)
[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 #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         mono_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 MonoString*
1427 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1428 {
1429         const CategoryDesc *cdesc;
1430         /* no support for counters on other machines */
1431         if (mono_string_compare_ascii (machine, "."))
1432                 return NULL;
1433         cdesc = find_category (category);
1434         if (!cdesc) {
1435                 SharedCategory *scat = find_custom_category (category);
1436                 if (!scat)
1437                         return NULL;
1438                 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1439         }
1440         return mono_string_new (mono_domain_get (), cdesc->help);
1441 }
1442
1443 /*
1444  * Check if the category named @category exists on @machine. If @counter is not NULL, return
1445  * TRUE only if a counter with that name exists in the category.
1446  */
1447 MonoBoolean
1448 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1449 {
1450         const CategoryDesc *cdesc;
1451         /* no support for counters on other machines */
1452         if (mono_string_compare_ascii (machine, "."))
1453                 return FALSE;
1454         cdesc = find_category (category);
1455         if (!cdesc) {
1456                 SharedCategory *scat = find_custom_category (category);
1457                 if (!scat)
1458                         return FALSE;
1459                 /* counter is allowed to be null */
1460                 if (!counter)
1461                         return TRUE;
1462                 /* search through the custom category */
1463                 return find_custom_counter (scat, counter) != NULL;
1464         }
1465         /* counter is allowed to be null */
1466         if (!counter)
1467                 return TRUE;
1468         if (get_counter_in_category (cdesc, counter))
1469                 return TRUE;
1470         return FALSE;
1471 }
1472
1473 /* C map of the type with the same name */
1474 typedef struct {
1475         MonoObject object;
1476         MonoString *help;
1477         MonoString *name;
1478         int type;
1479 } CounterCreationData;
1480
1481 /*
1482  * Since we'll keep a copy of the category per-process, we should also make sure
1483  * categories with the same name are compatible.
1484  */
1485 MonoBoolean
1486 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1487 {
1488         MonoError error;
1489         int result = FALSE;
1490         int i, size;
1491         int num_counters = mono_array_length (items);
1492         int counters_data_size;
1493         char *name = NULL;
1494         char *chelp = NULL;
1495         char **counter_info = NULL;
1496         char *p;
1497         SharedCategory *cat;
1498
1499         /* FIXME: ensure there isn't a category created already */
1500         name = mono_string_to_utf8_checked (category, &error);
1501         if (!mono_error_ok (&error))
1502                 goto failure;
1503         chelp = mono_string_to_utf8_checked (help, &error);
1504         if (!mono_error_ok (&error))
1505                 goto failure;
1506         counter_info = g_new0 (char*, num_counters * 2);
1507         /* calculate the size we need structure size + name/help + 2 0 string terminators */
1508         size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1509         for (i = 0; i < num_counters; ++i) {
1510                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1511                 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1512                 if (!mono_error_ok (&error))
1513                         goto failure;
1514                 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1515                 if (!mono_error_ok (&error))
1516                         goto failure;
1517                 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1518         }
1519         for (i = 0; i < num_counters * 2; ++i) {
1520                 if (!counter_info [i])
1521                         goto failure;
1522                 size += strlen (counter_info [i]) + 1;
1523         }
1524         size += 7;
1525         size &= ~7;
1526         counters_data_size = num_counters * 8; /* optimize for size later */
1527         if (size > 65535)
1528                 goto failure;
1529         perfctr_lock ();
1530         cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1531         if (!cat) {
1532                 perfctr_unlock ();
1533                 goto failure;
1534         }
1535         cat->num_counters = num_counters;
1536         cat->counters_data_size = counters_data_size;
1537         /* now copy the vaiable data */
1538         p = cat->name;
1539         strcpy (p, name);
1540         p += strlen (name) + 1;
1541         strcpy (p, chelp);
1542         p += strlen (chelp) + 1;
1543         for (i = 0; i < num_counters; ++i) {
1544                 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1545                 /* emit the SharedCounter structures */
1546                 *p++ = perfctr_type_compress (data->type);
1547                 *p++ = i;
1548                 strcpy (p, counter_info [i * 2]);
1549                 p += strlen (counter_info [i * 2]) + 1;
1550                 strcpy (p, counter_info [i * 2 + 1]);
1551                 p += strlen (counter_info [i * 2 + 1]) + 1;
1552         }
1553
1554         perfctr_unlock ();
1555         result = TRUE;
1556 failure:
1557         if (counter_info) {
1558                 for (i = 0; i < num_counters * 2; ++i) {
1559                         g_free (counter_info [i]);
1560                 }
1561                 g_free (counter_info);
1562         }
1563         g_free (name);
1564         g_free (chelp);
1565         mono_error_cleanup (&error);
1566         return result;
1567 }
1568
1569 int
1570 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1571 {
1572         MonoError error;
1573         const CategoryDesc *cdesc;
1574         SharedInstance *sinst;
1575         char *name;
1576         /* no support for counters on other machines */
1577         /*FIXME: machine appears to be wrong
1578         if (mono_string_compare_ascii (machine, "."))
1579                 return FALSE;*/
1580         cdesc = find_category (category);
1581         if (!cdesc) {
1582                 SharedCategory *scat;
1583                 scat = find_custom_category (category);
1584                 if (!scat)
1585                         return FALSE;
1586                 name = mono_string_to_utf8_checked (instance, &error);
1587                 if (mono_error_set_pending_exception (&error))
1588                         return FALSE;
1589                 sinst = find_custom_instance (scat, name);
1590                 g_free (name);
1591                 if (sinst)
1592                         return TRUE;
1593         } else {
1594                 /* FIXME: search instance */
1595         }
1596         return FALSE;
1597 }
1598
1599 MonoArray*
1600 mono_perfcounter_category_names (MonoString *machine)
1601 {
1602         MonoError error;
1603         int i;
1604         MonoArray *res;
1605         MonoDomain *domain = mono_domain_get ();
1606         GSList *custom_categories, *tmp;
1607         /* no support for counters on other machines */
1608         if (mono_string_compare_ascii (machine, ".")) {
1609                 res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1610                 mono_error_set_pending_exception (&error);
1611                 return res;
1612         }
1613         perfctr_lock ();
1614         custom_categories = get_custom_categories ();
1615         res = mono_array_new_checked (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories), &error);
1616         if (mono_error_set_pending_exception (&error)) {
1617                 perfctr_unlock ();
1618                 return NULL;
1619         }
1620
1621         for (i = 0; i < NUM_CATEGORIES; ++i) {
1622                 const CategoryDesc *cdesc = &predef_categories [i];
1623                 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1624         }
1625         for (tmp = custom_categories; tmp; tmp = tmp->next) {
1626                 SharedCategory *scat = (SharedCategory *)tmp->data;
1627                 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1628                 i++;
1629         }
1630         perfctr_unlock ();
1631         g_slist_free (custom_categories);
1632         return res;
1633 }
1634
1635 MonoArray*
1636 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1637 {
1638         MonoError error;
1639         int i;
1640         SharedCategory *scat;
1641         const CategoryDesc *cdesc;
1642         MonoArray *res;
1643         MonoDomain *domain = mono_domain_get ();
1644         /* no support for counters on other machines */
1645         if (mono_string_compare_ascii (machine, ".")) {
1646                 res =  mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1647                 mono_error_set_pending_exception (&error);
1648                 return res;
1649         }
1650         cdesc = find_category (category);
1651         if (cdesc) {
1652                 res = mono_array_new_checked (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter, &error);
1653                 if (mono_error_set_pending_exception (&error))
1654                         return NULL;
1655                 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1656                         const CounterDesc *desc = &predef_counters [i];
1657                         mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1658                 }
1659                 return res;
1660         }
1661         perfctr_lock ();
1662         scat = find_custom_category (category);
1663         if (scat) {
1664                 char *p = custom_category_counters (scat);
1665                 int i;
1666                 res = mono_array_new_checked (domain, mono_get_string_class (), scat->num_counters, &error);
1667                 if (mono_error_set_pending_exception (&error)) {
1668                         perfctr_unlock ();
1669                         return NULL;
1670                 }
1671
1672                 for (i = 0; i < scat->num_counters; ++i) {
1673                         mono_array_setref (res, i, mono_string_new (domain, p + 1));
1674                         p += 2; /* skip counter type */
1675                         p += strlen (p) + 1; /* skip counter name */
1676                         p += strlen (p) + 1; /* skip counter help */
1677                 }
1678                 perfctr_unlock ();
1679                 return res;
1680         }
1681         perfctr_unlock ();
1682         res = mono_array_new_checked (domain, mono_get_string_class (), 0, &error);
1683         mono_error_set_pending_exception (&error);
1684         return res;
1685 }
1686
1687 static MonoArray*
1688 get_string_array (void **array, int count, gboolean is_process, MonoError *error)
1689 {
1690         int i;
1691         MonoDomain *domain = mono_domain_get ();
1692         mono_error_init (error);
1693         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1694         return_val_if_nok (error, NULL);
1695         for (i = 0; i < count; ++i) {
1696                 char buf [128];
1697                 char *p;
1698                 if (is_process) {
1699                         char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1700                         p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1701                 } else {
1702                         sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1703                         p = buf;
1704                 }
1705                 mono_array_setref (res, i, mono_string_new (domain, p));
1706                 if (p != buf)
1707                         g_free (p);
1708         }
1709         return res;
1710 }
1711
1712 static MonoArray*
1713 get_string_array_of_strings (void **array, int count, MonoError *error)
1714 {
1715         int i;
1716         MonoDomain *domain = mono_domain_get ();
1717         mono_error_init (error);
1718         MonoArray * res = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), count, error);
1719         return_val_if_nok (error, NULL);
1720         for (i = 0; i < count; ++i) {
1721                 char* p = (char *)array[i];
1722                 mono_array_setref (res, i, mono_string_new (domain, p));
1723         }
1724
1725         return res;
1726 }
1727
1728 static MonoArray*
1729 get_mono_instances (MonoError *error)
1730 {
1731         int count = 64;
1732         int res;
1733         void **buf = NULL;
1734         MonoArray *array;
1735         mono_error_init (error);
1736         do {
1737                 count *= 2;
1738                 g_free (buf);
1739                 buf = g_new (void*, count);
1740                 res = mono_shared_area_instances (buf, count);
1741         } while (res == count);
1742         array = get_string_array (buf, res, TRUE, error);
1743         g_free (buf);
1744         return array;
1745 }
1746
1747 static MonoArray*
1748 get_cpu_instances (MonoError *error)
1749 {
1750         void **buf = NULL;
1751         int i, count;
1752         MonoArray *array;
1753         mono_error_init (error);
1754         count = mono_cpu_count () + 1; /* +1 for "_Total" */
1755         buf = g_new (void*, count);
1756         for (i = 0; i < count; ++i)
1757                 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1758         array = get_string_array (buf, count, FALSE, error);
1759         g_free (buf);
1760         mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1761         return array;
1762 }
1763
1764 static MonoArray*
1765 get_processes_instances (MonoError *error)
1766 {
1767         MonoArray *array;
1768         int count = 0;
1769         void **buf = mono_process_list (&count);
1770         mono_error_init (error);
1771         if (!buf)
1772                 return get_string_array (NULL, 0, FALSE, error);
1773         array = get_string_array (buf, count, TRUE, error);
1774         g_free (buf);
1775         return array;
1776 }
1777
1778 static MonoArray*
1779 get_networkinterface_instances (MonoError *error)
1780 {
1781         MonoArray *array;
1782         int count = 0;
1783         mono_error_init (error);
1784         void **buf = mono_networkinterface_list (&count);
1785         if (!buf)
1786                 return get_string_array_of_strings (NULL, 0, error);
1787         array = get_string_array_of_strings (buf, count, error);
1788         g_strfreev ((char **) buf);
1789         return array;
1790 }
1791
1792 static MonoArray*
1793 get_custom_instances (MonoString *category, MonoError *error)
1794 {
1795         SharedCategory *scat;
1796         mono_error_init (error);
1797         scat = find_custom_category (category);
1798         if (scat) {
1799                 GSList *list = get_custom_instances_list (scat);
1800                 GSList *tmp;
1801                 int i = 0;
1802                 MonoArray *array = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), g_slist_length (list), error);
1803                 if (!is_ok (error)) {
1804                         g_slist_free (list);
1805                         return NULL;
1806                 }
1807                 for (tmp = list; tmp; tmp = tmp->next) {
1808                         SharedInstance *inst = (SharedInstance *)tmp->data;
1809                         mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1810                         i++;
1811                 }
1812                 g_slist_free (list);
1813                 return array;
1814         }
1815         return mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, error);
1816 }
1817
1818 MonoArray*
1819 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1820 {
1821         MonoError error;
1822         const CategoryDesc* cat;
1823         MonoArray *result = NULL;
1824         if (mono_string_compare_ascii (machine, ".")) {
1825                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1826                 mono_error_set_pending_exception (&error);
1827                 return result;
1828         }
1829         
1830         cat = find_category (category);
1831         if (!cat) {
1832                 MonoArray *result = get_custom_instances (category, &error);
1833                 mono_error_set_pending_exception (&error);
1834                 return result;
1835         }
1836         switch (cat->instance_type) {
1837         case MonoInstance:
1838                 result = get_mono_instances (&error);
1839                 break;
1840         case CPUInstance:
1841                 result = get_cpu_instances (&error);
1842                 break;
1843         case ProcessInstance:
1844                 result = get_processes_instances (&error);
1845                 break;
1846         case NetworkInterfaceInstance:
1847                 result = get_networkinterface_instances (&error);
1848                 break;
1849         case ThreadInstance:
1850         default:
1851                 result = mono_array_new_checked (mono_domain_get (), mono_get_string_class (), 0, &error);
1852         }
1853         mono_error_set_pending_exception (&error);
1854         return result;
1855 }
1856
1857 typedef struct {
1858         PerfCounterEnumCallback cb;
1859         void *data;
1860 } PerfCounterForeachData;
1861
1862 static gboolean
1863 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1864 {
1865         int i;
1866         char *p, *name;
1867         unsigned char type;
1868         void *addr;
1869         SharedCategory *cat;
1870         SharedCounter *counter;
1871         SharedInstance *inst;
1872         PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1873
1874         if (header->ftype == FTYPE_CATEGORY) {
1875                 cat = (SharedCategory*)header;
1876
1877                 p = cat->name;
1878                 p += strlen (p) + 1; /* skip category name */
1879                 p += strlen (p) + 1; /* skip category help */
1880
1881                 for (i = 0; i < cat->num_counters; ++i) {
1882                         counter = (SharedCounter*) p;
1883                         type = (unsigned char)*p++;
1884                         /* seq_num = (int)* */ p++;
1885                         name = p;
1886                         p += strlen (p) + 1;
1887                         /* help = p; */
1888                         p += strlen (p) + 1;
1889
1890                         inst = custom_get_instance (cat, counter, name);
1891                         if (!inst)
1892                                 return FALSE;
1893                         addr = custom_get_value_address (counter, inst);
1894                         if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1895                                 return FALSE;
1896                 }
1897         }
1898
1899         return TRUE;
1900 }
1901
1902 void
1903 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1904 {
1905         PerfCounterForeachData foreach_data = { cb, data };
1906
1907         perfctr_lock ();
1908
1909         foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1910
1911         perfctr_unlock ();
1912 }
1913
1914 #else
1915 void*
1916 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1917 {
1918         g_assert_not_reached ();
1919 }
1920
1921 MonoBoolean
1922 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1923 {
1924         g_assert_not_reached ();
1925 }
1926
1927 gint64
1928 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1929 {
1930         g_assert_not_reached ();
1931 }
1932
1933 void
1934 mono_perfcounter_free_data (void *impl)
1935 {
1936         g_assert_not_reached ();
1937 }
1938
1939 /* Category icalls */
1940 MonoBoolean
1941 mono_perfcounter_category_del (MonoString *name)
1942 {
1943         g_assert_not_reached ();
1944 }
1945
1946 MonoString*
1947 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1948 {
1949         g_assert_not_reached ();
1950 }
1951
1952 MonoBoolean
1953 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1954 {
1955         g_assert_not_reached ();
1956 }
1957
1958 MonoBoolean
1959 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1960 {
1961         g_assert_not_reached ();
1962 }
1963
1964 int
1965 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1966 {
1967         g_assert_not_reached ();
1968 }
1969
1970 MonoArray*
1971 mono_perfcounter_category_names (MonoString *machine)
1972 {
1973         g_assert_not_reached ();
1974 }
1975
1976 MonoArray*
1977 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1978 {
1979         g_assert_not_reached ();
1980 }
1981
1982 MonoArray*
1983 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1984 {
1985         g_assert_not_reached ();
1986 }
1987 #endif