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