[sgen] One internal allocator per worker thread, to get rid of locking.
[mono-project/dkf.git] / mono / metadata / mono-perfcounters.c
blob35e271b65f1637f54f1cf158e20fdc716cd4c9db
1 /*
2 * mono-perfcounters.c
4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
9 */
11 #include "config.h"
12 #include <time.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #if defined (__OpenBSD__)
19 #include <sys/param.h>
20 #endif
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
23 #endif
24 #ifdef HAVE_SYS_TIME_H
25 #include <sys/time.h>
26 #endif
27 #if defined (__NetBSD__) || defined (__APPLE__)
28 #include <sys/sysctl.h>
29 #endif
30 #include "metadata/mono-perfcounters.h"
31 #include "metadata/appdomain.h"
32 #include "metadata/object-internals.h"
33 /* for mono_stats */
34 #include "metadata/class-internals.h"
35 #include "utils/mono-time.h"
36 #include "utils/mono-mmap.h"
37 #include "utils/mono-proclib.h"
38 #include "utils/mono-networkinterfaces.h"
39 #include "utils/mono-error-internals.h"
40 #include <mono/io-layer/io-layer.h>
42 /* map of CounterSample.cs */
43 struct _MonoCounterSample {
44 gint64 rawValue;
45 gint64 baseValue;
46 gint64 counterFrequency;
47 gint64 systemFrequency;
48 gint64 timeStamp;
49 gint64 timeStamp100nSec;
50 gint64 counterTimeStamp;
51 int counterType;
54 /* map of PerformanceCounterType.cs */
55 enum {
56 NumberOfItemsHEX32=0x00000000,
57 NumberOfItemsHEX64=0x00000100,
58 NumberOfItems32=0x00010000,
59 NumberOfItems64=0x00010100,
60 CounterDelta32=0x00400400,
61 CounterDelta64=0x00400500,
62 SampleCounter=0x00410400,
63 CountPerTimeInterval32=0x00450400,
64 CountPerTimeInterval64=0x00450500,
65 RateOfCountsPerSecond32=0x10410400,
66 RateOfCountsPerSecond64=0x10410500,
67 RawFraction=0x20020400,
68 CounterTimer=0x20410500,
69 Timer100Ns=0x20510500,
70 SampleFraction=0x20C20400,
71 CounterTimerInverse=0x21410500,
72 Timer100NsInverse=0x21510500,
73 CounterMultiTimer=0x22410500,
74 CounterMultiTimer100Ns=0x22510500,
75 CounterMultiTimerInverse=0x23410500,
76 CounterMultiTimer100NsInverse=0x23510500,
77 AverageTimer32=0x30020400,
78 ElapsedTime=0x30240500,
79 AverageCount64=0x40020500,
80 SampleBase=0x40030401,
81 AverageBase=0x40030402,
82 RawBase=0x40030403,
83 CounterMultiBase=0x42030500
86 /* maps a small integer type to the counter types above */
87 static const int
88 simple_type_to_type [] = {
89 NumberOfItemsHEX32, NumberOfItemsHEX64,
90 NumberOfItems32, NumberOfItems64,
91 CounterDelta32, CounterDelta64,
92 SampleCounter, CountPerTimeInterval32,
93 CountPerTimeInterval64, RateOfCountsPerSecond32,
94 RateOfCountsPerSecond64, RawFraction,
95 CounterTimer, Timer100Ns,
96 SampleFraction, CounterTimerInverse,
97 Timer100NsInverse, CounterMultiTimer,
98 CounterMultiTimer100Ns, CounterMultiTimerInverse,
99 CounterMultiTimer100NsInverse, AverageTimer32,
100 ElapsedTime, AverageCount64,
101 SampleBase, AverageBase,
102 RawBase, CounterMultiBase
105 enum {
106 SingleInstance,
107 MultiInstance,
108 CatTypeUnknown = -1
111 enum {
112 ProcessInstance,
113 ThreadInstance,
114 CPUInstance,
115 MonoInstance,
116 NetworkInterfaceInstance,
117 CustomInstance
120 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
121 #define PERFCTR_COUNTER(id,name,help,type,field)
122 enum {
123 #include "mono-perfcounters-def.h"
124 NUM_CATEGORIES
127 #undef PERFCTR_CAT
128 #undef PERFCTR_COUNTER
129 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
130 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
131 /* each counter is assigned an id starting from 0 inside the category */
132 enum {
133 #include "mono-perfcounters-def.h"
134 END_COUNTERS
137 #undef PERFCTR_CAT
138 #undef PERFCTR_COUNTER
139 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
140 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
141 /* this is used just to count the number of counters */
142 enum {
143 #include "mono-perfcounters-def.h"
144 NUM_COUNTERS
147 static CRITICAL_SECTION perfctr_mutex;
148 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
149 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
151 typedef struct {
152 char reserved [16];
153 int size;
154 unsigned short counters_start;
155 unsigned short counters_size;
156 unsigned short data_start;
157 MonoPerfCounters counters;
158 char data [1];
159 } MonoSharedArea;
162 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
163 basic stanza:
164 struct stanza_header {
165 byte stanza_type; // FTYPE_*
166 byte other_info;
167 ushort stanza_length; // includeas header
168 ... data ...
171 // strings are utf8
172 // perfcat and perfinstance are 4-bytes aligned
173 struct perfcat {
174 byte typeidx;
175 byte categorytype;
176 ushort length; // includes the counters
177 ushort num_counters;
178 ushort counters_data_size;
179 int num_instances;
180 char name[]; // null terminated
181 char help[]; // null terminated
182 // perfcounters follow
184 byte countertype;
185 char name[]; // null terminated
186 char help[]; // null terminated
188 0-byte
191 struct perfinstance {
192 byte typeidx;
193 byte data_offset; // offset of counters from beginning of struct
194 ushort length;
195 uint category_offset; // offset of category in the shared area
196 char name[]; // null terminated
197 // data follows: this is always 8-byte aligned
202 enum {
203 FTYPE_CATEGORY = 'C',
204 FTYPE_DELETED = 'D',
205 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
206 FTYPE_INSTANCE = 'I',
207 FTYPE_DIRTY = 'd',
208 FTYPE_END = 0
211 typedef struct {
212 unsigned char ftype;
213 unsigned char extra;
214 unsigned short size;
215 } SharedHeader;
217 typedef struct {
218 SharedHeader header;
219 unsigned short num_counters;
220 unsigned short counters_data_size;
221 int num_instances;
222 /* variable length data follows */
223 char name [1];
224 } SharedCategory;
226 typedef struct {
227 SharedHeader header;
228 unsigned int category_offset;
229 /* variable length data follows */
230 char instance_name [1];
231 } SharedInstance;
233 typedef struct {
234 unsigned char type;
235 guint8 seq_num;
236 /* variable length data follows */
237 char name [1];
238 } SharedCounter;
240 typedef struct {
241 const char *name;
242 const char *help;
243 unsigned char id;
244 signed int type : 2;
245 unsigned int instance_type : 6;
246 short first_counter;
247 } CategoryDesc;
249 typedef struct {
250 const char *name;
251 const char *help;
252 short id;
253 unsigned short offset; // offset inside MonoPerfCounters
254 int type;
255 } CounterDesc;
257 #undef PERFCTR_CAT
258 #undef PERFCTR_COUNTER
259 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
260 #define PERFCTR_COUNTER(id,name,help,type,field)
261 static const CategoryDesc
262 predef_categories [] = {
263 #include "mono-perfcounters-def.h"
264 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
267 #undef PERFCTR_CAT
268 #undef PERFCTR_COUNTER
269 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
270 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
271 static const CounterDesc
272 predef_counters [] = {
273 #include "mono-perfcounters-def.h"
274 {NULL, NULL, -1, 0, 0}
278 * We have several different classes of counters:
279 * *) system counters
280 * *) runtime counters
281 * *) remote counters
282 * *) user-defined counters
283 * *) windows counters (the implementation on windows will use this)
285 * To easily handle the differences we create a vtable for each class that contains the
286 * function pointers with the actual implementation to access the counters.
288 typedef struct _ImplVtable ImplVtable;
290 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
291 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
292 typedef void (*CleanupFunc) (ImplVtable *vtable);
294 struct _ImplVtable {
295 void *arg;
296 SampleFunc sample;
297 UpdateFunc update;
298 CleanupFunc cleanup;
301 typedef struct {
302 int id;
303 char *name;
304 } NetworkVtableArg;
306 typedef struct {
307 ImplVtable vtable;
308 MonoPerfCounters *counters;
309 int pid;
310 } PredefVtable;
312 typedef struct {
313 ImplVtable vtable;
314 SharedInstance *instance_desc;
315 SharedCounter *counter_desc;
316 } CustomVTable;
318 static ImplVtable*
319 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
321 ImplVtable *vtable = g_new0 (ImplVtable, 1);
322 vtable->arg = arg;
323 vtable->sample = sample;
324 vtable->update = update;
325 return vtable;
328 MonoPerfCounters *mono_perfcounters = NULL;
329 static MonoSharedArea *shared_area = NULL;
331 typedef struct {
332 void *sarea;
333 int refcount;
334 } ExternalSArea;
336 /* maps a pid to a ExternalSArea pointer */
337 static GHashTable *pid_to_shared_area = NULL;
339 static MonoSharedArea *
340 load_sarea_for_pid (int pid)
342 ExternalSArea *data;
343 MonoSharedArea *area = NULL;
345 perfctr_lock ();
346 if (pid_to_shared_area == NULL)
347 pid_to_shared_area = g_hash_table_new (NULL, NULL);
348 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
349 if (!data) {
350 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
351 if (area) {
352 data = g_new (ExternalSArea, 1);
353 data->sarea = area;
354 data->refcount = 1;
355 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
357 } else {
358 area = data->sarea;
359 data->refcount ++;
361 perfctr_unlock ();
362 return area;
365 static void
366 unref_pid_unlocked (int pid)
368 ExternalSArea *data;
369 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
370 if (data) {
371 data->refcount--;
372 if (!data->refcount) {
373 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
374 mono_shared_area_unload (data->sarea);
375 g_free (data);
380 static void
381 predef_cleanup (ImplVtable *vtable)
383 PredefVtable *vt = (PredefVtable*)vtable;
384 /* ExternalSArea *data; */
386 perfctr_lock ();
387 if (!pid_to_shared_area) {
388 perfctr_unlock ();
389 return;
391 unref_pid_unlocked (vt->pid);
392 perfctr_unlock ();
395 static guint64
396 mono_determine_physical_ram_size (void)
398 #if defined (TARGET_WIN32)
399 MEMORYSTATUSEX memstat;
401 memstat.dwLength = sizeof (memstat);
402 GlobalMemoryStatusEx (&memstat);
403 return (guint64)memstat.ullTotalPhys;
404 #elif defined (__NetBSD__) || defined (__APPLE__)
405 #ifdef __NetBSD__
406 unsigned long value;
407 #else
408 guint64 value;
409 #endif
410 int mib[2] = {
411 CTL_HW,
412 #ifdef __NetBSD__
413 HW_PHYSMEM
414 #else
415 HW_MEMSIZE
416 #endif
418 size_t size_sys = sizeof (value);
420 sysctl (mib, 2, &value, &size_sys, NULL, 0);
421 if (value == 0)
422 return 134217728;
424 return (guint64)value;
425 #elif defined (HAVE_SYSCONF)
426 guint64 page_size = 0, num_pages = 0;
428 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
429 * reports invalid values, please add your OS specific code below. */
430 #ifdef _SC_PAGESIZE
431 page_size = (guint64)sysconf (_SC_PAGESIZE);
432 #endif
434 #ifdef _SC_PHYS_PAGES
435 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
436 #endif
438 if (!page_size || !num_pages) {
439 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
440 return 134217728;
443 return page_size * num_pages;
444 #else
445 return 134217728;
446 #endif
449 void
450 mono_perfcounters_init (void)
452 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
453 d_offset += 7;
454 d_offset &= ~7;
456 InitializeCriticalSection (&perfctr_mutex);
458 shared_area = mono_shared_area ();
459 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
460 shared_area->counters_size = sizeof (MonoPerfCounters);
461 shared_area->data_start = d_offset;
462 shared_area->size = 4096;
463 mono_perfcounters = &shared_area->counters;
466 static int
467 perfctr_type_compress (int type)
469 int i;
470 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
471 if (simple_type_to_type [i] == type)
472 return i;
474 /* NumberOfItems32 */
475 return 2;
478 static unsigned char*
479 shared_data_find_room (int size)
481 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
482 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
484 size += 7;
485 size &= ~7;
486 while (p < end) {
487 unsigned short *next;
488 if (*p == FTYPE_END) {
489 if (size < (end - p))
490 return p;
491 return NULL;
493 if (p + 4 > end)
494 return NULL;
495 next = (unsigned short*)(p + 2);
496 if (*p == FTYPE_DELETED) {
497 /* we reuse only if it's the same size */
498 if (*next == size) {
499 return p;
502 p += *next;
504 return NULL;
507 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
509 static void
510 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
512 while (p < end) {
513 unsigned short *next;
514 if (p + 4 > end)
515 return;
516 next = (unsigned short*)(p + 2);
517 if (!func ((SharedHeader*)p, data))
518 return;
519 if (*p == FTYPE_END)
520 return;
521 p += *next;
525 static void
526 foreach_shared_item (SharedFunc func, void *data)
528 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
529 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
531 foreach_shared_item_in_area (p, end, func, data);
534 static int
535 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
537 /* FIXME: make this case insensitive */
538 guint16 *strc = mono_string_chars (str);
539 while (*strc == *ascii_str++) {
540 if (*strc == 0)
541 return 0;
542 strc++;
544 return *strc - *(const unsigned char *)(ascii_str - 1);
547 typedef struct {
548 MonoString *name;
549 SharedCategory *cat;
550 } CatSearch;
552 static gboolean
553 category_search (SharedHeader *header, void *data)
555 CatSearch *search = data;
556 if (header->ftype == FTYPE_CATEGORY) {
557 SharedCategory *cat = (SharedCategory*)header;
558 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
559 search->cat = cat;
560 return FALSE;
563 return TRUE;
566 static SharedCategory*
567 find_custom_category (MonoString *name)
569 CatSearch search;
570 search.name = name;
571 search.cat = NULL;
572 foreach_shared_item (category_search, &search);
573 return search.cat;
576 static gboolean
577 category_collect (SharedHeader *header, void *data)
579 GSList **list = data;
580 if (header->ftype == FTYPE_CATEGORY) {
581 *list = g_slist_prepend (*list, header);
583 return TRUE;
586 static GSList*
587 get_custom_categories (void) {
588 GSList *list = NULL;
589 foreach_shared_item (category_collect, &list);
590 return list;
593 static char*
594 custom_category_counters (SharedCategory* cat)
596 char *p = cat->name + strlen (cat->name) + 1;
597 p += strlen (p) + 1; /* skip category help */
598 return p;
601 static SharedCounter*
602 find_custom_counter (SharedCategory* cat, MonoString *name)
604 int i;
605 char *p = custom_category_counters (cat);
606 for (i = 0; i < cat->num_counters; ++i) {
607 SharedCounter *counter = (SharedCounter*)p;
608 if (mono_string_compare_ascii (name, counter->name) == 0)
609 return counter;
610 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
611 p += strlen (p) + 1; /* skip counter help */
613 return NULL;
616 typedef struct {
617 unsigned int cat_offset;
618 SharedCategory* cat;
619 MonoString *instance;
620 SharedInstance* result;
621 GSList *list;
622 } InstanceSearch;
624 static gboolean
625 instance_search (SharedHeader *header, void *data)
627 InstanceSearch *search = data;
628 if (header->ftype == FTYPE_INSTANCE) {
629 SharedInstance *ins = (SharedInstance*)header;
630 if (search->cat_offset == ins->category_offset) {
631 if (search->instance) {
632 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
633 search->result = ins;
634 return FALSE;
636 } else {
637 search->list = g_slist_prepend (search->list, ins);
641 return TRUE;
644 static SharedInstance*
645 find_custom_instance (SharedCategory* cat, MonoString *instance)
647 InstanceSearch search;
648 search.cat_offset = (char*)cat - (char*)shared_area;
649 search.cat = cat;
650 search.instance = instance;
651 search.list = NULL;
652 search.result = NULL;
653 foreach_shared_item (instance_search, &search);
654 return search.result;
657 static GSList*
658 get_custom_instances_list (SharedCategory* cat)
660 InstanceSearch search;
661 search.cat_offset = (char*)cat - (char*)shared_area;
662 search.cat = cat;
663 search.instance = NULL;
664 search.list = NULL;
665 search.result = NULL;
666 foreach_shared_item (instance_search, &search);
667 return search.list;
670 static char*
671 custom_category_help (SharedCategory* cat)
673 return cat->name + strlen (cat->name) + 1;
676 static const CounterDesc*
677 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
679 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
680 const CounterDesc *end = &predef_counters [desc [1].first_counter];
681 for (; cdesc < end; ++cdesc) {
682 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
683 return cdesc;
685 return NULL;
688 /* fill the info in sample (except the raw value) */
689 static void
690 fill_sample (MonoCounterSample *sample)
692 sample->timeStamp = mono_100ns_ticks ();
693 sample->timeStamp100nSec = sample->timeStamp;
694 sample->counterTimeStamp = sample->timeStamp;
695 sample->counterFrequency = 10000000;
696 sample->systemFrequency = 10000000;
697 // the real basevalue needs to be get from a different counter...
698 sample->baseValue = 0;
701 static int
702 id_from_string (MonoString *instance, gboolean is_process)
704 int id = -1;
705 if (mono_string_length (instance)) {
706 char *id_str = mono_string_to_utf8 (instance);
707 char *end;
708 id = strtol (id_str, &end, 0);
709 if (end == id_str && !is_process)
710 id = -1;
711 g_free (id_str);
713 return id;
716 static MonoBoolean
717 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
719 MonoProcessError error;
720 int id = GPOINTER_TO_INT (vtable->arg);
721 int pid = id >> 5;
722 id &= 0x1f;
723 if (!only_value) {
724 fill_sample (sample);
725 sample->baseValue = 1;
727 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
728 switch (id) {
729 case COUNTER_CPU_USER_TIME:
730 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
731 return TRUE;
732 case COUNTER_CPU_PRIV_TIME:
733 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
734 return TRUE;
735 case COUNTER_CPU_INTR_TIME:
736 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
737 return TRUE;
738 case COUNTER_CPU_DCP_TIME:
739 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
740 return TRUE;
741 case COUNTER_CPU_PROC_TIME:
742 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
743 return TRUE;
745 return FALSE;
748 static void*
749 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
751 int id = id_from_string (instance, FALSE) << 5;
752 const CounterDesc *cdesc;
753 *custom = FALSE;
754 /* increase the shift above and the mask also in the implementation functions */
755 //g_assert (32 > desc [1].first_counter - desc->first_counter);
756 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
757 *type = cdesc->type;
758 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
760 return NULL;
763 static MonoBoolean
764 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
766 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
767 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
768 if (!only_value) {
769 fill_sample (sample);
772 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
773 switch (narg->id) {
774 case COUNTER_NETWORK_BYTESRECSEC:
775 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
776 break;
777 case COUNTER_NETWORK_BYTESSENTSEC:
778 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
779 break;
780 case COUNTER_NETWORK_BYTESTOTALSEC:
781 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
782 break;
785 if (error == MONO_NETWORK_ERROR_NONE)
786 return TRUE;
787 else
788 return FALSE;
791 static void
792 network_cleanup (ImplVtable *vtable)
794 NetworkVtableArg *narg;
796 if (vtable == NULL)
797 return;
799 narg = vtable->arg;
800 if (narg == NULL)
801 return;
803 g_free (narg->name);
804 narg->name = NULL;
805 g_free (narg);
806 vtable->arg = NULL;
809 static void*
810 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
812 const CounterDesc *cdesc;
813 NetworkVtableArg *narg;
814 ImplVtable *vtable;
815 char *instance_name;
817 *custom = FALSE;
818 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
819 instance_name = mono_string_to_utf8 (instance);
820 narg = g_new0 (NetworkVtableArg, 1);
821 narg->id = cdesc->id;
822 narg->name = instance_name;
823 *type = cdesc->type;
824 vtable = create_vtable (narg, get_network_counter, NULL);
825 vtable->cleanup = network_cleanup;
826 return vtable;
828 return NULL;
831 static MonoBoolean
832 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
834 int id = GPOINTER_TO_INT (vtable->arg);
835 int pid = id >> 5;
836 if (pid < 0)
837 return FALSE;
838 id &= 0x1f;
839 if (!only_value) {
840 fill_sample (sample);
841 sample->baseValue = 1;
843 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
844 switch (id) {
845 case COUNTER_PROC_USER_TIME:
846 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
847 return TRUE;
848 case COUNTER_PROC_PRIV_TIME:
849 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
850 return TRUE;
851 case COUNTER_PROC_PROC_TIME:
852 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
853 return TRUE;
854 case COUNTER_PROC_THREADS:
855 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
856 return TRUE;
857 case COUNTER_PROC_VBYTES:
858 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
859 return TRUE;
860 case COUNTER_PROC_WSET:
861 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
862 return TRUE;
863 case COUNTER_PROC_PBYTES:
864 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
865 return TRUE;
867 return FALSE;
870 static void*
871 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
873 int id = id_from_string (instance, TRUE) << 5;
874 const CounterDesc *cdesc;
875 *custom = FALSE;
876 /* increase the shift above and the mask also in the implementation functions */
877 //g_assert (32 > desc [1].first_counter - desc->first_counter);
878 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
879 *type = cdesc->type;
880 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
882 return NULL;
885 static MonoBoolean
886 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
888 int id = GPOINTER_TO_INT (vtable->arg);
889 if (!only_value) {
890 fill_sample (sample);
891 sample->baseValue = 1;
893 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
894 switch (id) {
895 case COUNTER_MEM_NUM_OBJECTS:
896 sample->rawValue = mono_stats.new_object_count;
897 return TRUE;
898 case COUNTER_MEM_PHYS_TOTAL:
899 sample->rawValue = mono_determine_physical_ram_size ();;
900 return TRUE;
902 return FALSE;
905 static void*
906 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
908 const CounterDesc *cdesc;
909 *custom = FALSE;
910 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
911 *type = cdesc->type;
912 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
914 return NULL;
917 static MonoBoolean
918 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
920 PredefVtable *vt = (PredefVtable *)vtable;
921 const CounterDesc *desc;
922 int cat_id = GPOINTER_TO_INT (vtable->arg);
923 int id = cat_id >> 16;
924 cat_id &= 0xffff;
925 if (!only_value) {
926 fill_sample (sample);
927 sample->baseValue = 1;
929 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
930 sample->counterType = desc->type;
931 /* FIXME: check that the offset fits inside imported counters */
932 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
933 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
934 return TRUE;
937 static ImplVtable*
938 predef_vtable (void *arg, MonoString *instance)
940 MonoSharedArea *area;
941 PredefVtable *vtable;
942 char *pids = mono_string_to_utf8 (instance);
943 int pid;
945 pid = atoi (pids);
946 g_free (pids);
947 area = load_sarea_for_pid (pid);
948 if (!area)
949 return NULL;
951 vtable = g_new (PredefVtable, 1);
952 vtable->vtable.arg = arg;
953 vtable->vtable.sample = predef_readonly_counter;
954 vtable->vtable.cleanup = predef_cleanup;
955 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
956 vtable->pid = pid;
958 return (ImplVtable*)vtable;
961 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
962 * this needs some way to set sample->counterType as well, though.
964 static MonoBoolean
965 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
967 int cat_id = GPOINTER_TO_INT (vtable->arg);
968 int id = cat_id >> 16;
969 cat_id &= 0xffff;
970 if (!only_value) {
971 fill_sample (sample);
972 sample->baseValue = 1;
974 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
975 switch (cat_id) {
976 case CATEGORY_EXC:
977 switch (id) {
978 case COUNTER_EXC_THROWN:
979 sample->rawValue = mono_perfcounters->exceptions_thrown;
980 return TRUE;
982 break;
983 case CATEGORY_ASPNET:
984 switch (id) {
985 case COUNTER_ASPNET_REQ_Q:
986 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
987 return TRUE;
988 case COUNTER_ASPNET_REQ_TOTAL:
989 sample->rawValue = mono_perfcounters->aspnet_requests;
990 return TRUE;
992 break;
993 case CATEGORY_THREADPOOL:
994 switch (id) {
995 case COUNTER_THREADPOOL_WORKITEMS:
996 sample->rawValue = mono_perfcounters->threadpool_workitems;
997 return TRUE;
998 case COUNTER_THREADPOOL_IOWORKITEMS:
999 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1000 return TRUE;
1001 case COUNTER_THREADPOOL_THREADS:
1002 sample->rawValue = mono_perfcounters->threadpool_threads;
1003 return TRUE;
1004 case COUNTER_THREADPOOL_IOTHREADS:
1005 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1006 return TRUE;
1008 break;
1010 return FALSE;
1013 static gint64
1014 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1016 guint32 *volatile ptr = NULL;
1017 gint64 *volatile ptr64 = NULL;
1018 int cat_id = GPOINTER_TO_INT (vtable->arg);
1019 int id = cat_id >> 16;
1020 cat_id &= 0xffff;
1021 switch (cat_id) {
1022 case CATEGORY_ASPNET:
1023 switch (id) {
1024 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1025 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1027 break;
1028 case CATEGORY_THREADPOOL:
1029 switch (id) {
1030 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1031 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1032 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1033 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1035 break;
1037 if (ptr) {
1038 if (do_incr) {
1039 if (value == 1)
1040 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1041 if (value == -1)
1042 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1044 *ptr += value;
1045 return *ptr;
1047 /* this can be non-atomic */
1048 *ptr = value;
1049 return value;
1050 } else if (ptr64) {
1051 if (do_incr) {
1052 /* FIXME: we need to do this atomically */
1053 /* No InterlockedIncrement64() yet */
1055 if (value == 1)
1056 return InterlockedIncrement64 (ptr);
1057 if (value == -1)
1058 return InterlockedDecrement64 (ptr);
1061 *ptr64 += value;
1062 return *ptr64;
1064 /* this can be non-atomic */
1065 *ptr64 = value;
1066 return value;
1068 return 0;
1071 static void*
1072 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1074 const CounterDesc *cdesc;
1075 *custom = TRUE;
1076 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1077 *type = cdesc->type;
1078 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1079 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1080 else
1081 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1083 return NULL;
1086 static MonoBoolean
1087 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1089 CustomVTable *counter_data = (CustomVTable *)vtable;
1090 if (!only_value) {
1091 fill_sample (sample);
1092 sample->baseValue = 1;
1094 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1095 if (!vtable->arg)
1096 sample->rawValue = 0;
1097 else
1098 sample->rawValue = *(guint64*)vtable->arg;
1099 return TRUE;
1102 static gint64
1103 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1105 /* FIXME: check writability */
1106 guint64 *ptr = vtable->arg;
1107 if (ptr) {
1108 if (do_incr) {
1109 /* FIXME: we need to do this atomically */
1110 *ptr += value;
1111 return *ptr;
1113 /* this can be non-atomic */
1114 *ptr = value;
1115 return value;
1117 return 0;
1120 static SharedInstance*
1121 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
1123 SharedInstance* inst;
1124 unsigned char *ptr;
1125 char *p;
1126 int size, data_offset;
1127 char *name;
1128 inst = find_custom_instance (cat, instance);
1129 if (inst)
1130 return inst;
1131 name = mono_string_to_utf8 (instance);
1132 size = sizeof (SharedInstance) + strlen (name);
1133 size += 7;
1134 size &= ~7;
1135 data_offset = size;
1136 size += (sizeof (guint64) * cat->num_counters);
1137 perfctr_lock ();
1138 ptr = shared_data_find_room (size);
1139 if (!ptr) {
1140 perfctr_unlock ();
1141 g_free (name);
1142 return NULL;
1144 inst = (SharedInstance*)ptr;
1145 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
1146 inst->header.size = size;
1147 inst->category_offset = (char*)cat - (char*)shared_area;
1148 cat->num_instances++;
1149 /* now copy the variable data */
1150 p = inst->instance_name;
1151 strcpy (p, name);
1152 p += strlen (name) + 1;
1153 inst->header.ftype = FTYPE_INSTANCE;
1154 perfctr_unlock ();
1155 g_free (name);
1157 return inst;
1160 static ImplVtable*
1161 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1163 CustomVTable* vtable;
1164 vtable = g_new0 (CustomVTable, 1);
1165 vtable->vtable.arg = data;
1166 vtable->vtable.sample = custom_writable_counter;
1167 vtable->vtable.update = custom_writable_update;
1168 vtable->instance_desc = inst;
1169 vtable->counter_desc = scounter;
1171 return (ImplVtable*)vtable;
1174 static void*
1175 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1177 SharedCounter *scounter;
1178 SharedInstance* inst;
1179 int size;
1181 scounter = find_custom_counter (cat, counter);
1182 if (!scounter)
1183 return NULL;
1184 *type = simple_type_to_type [scounter->type];
1185 inst = custom_get_instance (cat, scounter, instance);
1186 if (!inst)
1187 return NULL;
1188 size = sizeof (SharedInstance) + strlen (inst->instance_name);
1189 size += 7;
1190 size &= ~7;
1191 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1194 static const CategoryDesc*
1195 find_category (MonoString *category)
1197 int i;
1198 for (i = 0; i < NUM_CATEGORIES; ++i) {
1199 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1200 return &predef_categories [i];
1202 return NULL;
1205 void*
1206 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1207 MonoString* machine, int *type, MonoBoolean *custom)
1209 const CategoryDesc *cdesc;
1210 /* no support for counters on other machines */
1211 if (mono_string_compare_ascii (machine, "."))
1212 return NULL;
1213 cdesc = find_category (category);
1214 if (!cdesc) {
1215 SharedCategory *scat = find_custom_category (category);
1216 if (!scat)
1217 return NULL;
1218 *custom = TRUE;
1219 return custom_get_impl (scat, counter, instance, type);
1221 switch (cdesc->id) {
1222 case CATEGORY_CPU:
1223 return cpu_get_impl (counter, instance, type, custom);
1224 case CATEGORY_PROC:
1225 return process_get_impl (counter, instance, type, custom);
1226 case CATEGORY_MONO_MEM:
1227 return mono_mem_get_impl (counter, instance, type, custom);
1228 case CATEGORY_NETWORK:
1229 return network_get_impl (counter, instance, type, custom);
1230 case CATEGORY_JIT:
1231 case CATEGORY_EXC:
1232 case CATEGORY_GC:
1233 case CATEGORY_REMOTING:
1234 case CATEGORY_LOADING:
1235 case CATEGORY_THREAD:
1236 case CATEGORY_INTEROP:
1237 case CATEGORY_SECURITY:
1238 case CATEGORY_ASPNET:
1239 case CATEGORY_THREADPOOL:
1240 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1242 return NULL;
1245 MonoBoolean
1246 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1248 ImplVtable *vtable = impl;
1249 if (vtable && vtable->sample)
1250 return vtable->sample (vtable, only_value, sample);
1251 return FALSE;
1254 gint64
1255 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1257 ImplVtable *vtable = impl;
1258 if (vtable && vtable->update)
1259 return vtable->update (vtable, do_incr, value);
1260 return 0;
1263 void
1264 mono_perfcounter_free_data (void *impl)
1266 ImplVtable *vtable = impl;
1267 if (vtable && vtable->cleanup)
1268 vtable->cleanup (vtable);
1269 g_free (impl);
1272 /* Category icalls */
1273 MonoBoolean
1274 mono_perfcounter_category_del (MonoString *name)
1276 const CategoryDesc *cdesc;
1277 SharedCategory *cat;
1278 cdesc = find_category (name);
1279 /* can't delete a predefined category */
1280 if (cdesc)
1281 return FALSE;
1282 perfctr_lock ();
1283 cat = find_custom_category (name);
1284 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1285 if (!cat || cat->num_instances) {
1286 perfctr_unlock ();
1287 return FALSE;
1289 cat->header.ftype = FTYPE_DELETED;
1290 perfctr_unlock ();
1291 return TRUE;
1294 MonoString*
1295 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1297 const CategoryDesc *cdesc;
1298 /* no support for counters on other machines */
1299 if (mono_string_compare_ascii (machine, "."))
1300 return NULL;
1301 cdesc = find_category (category);
1302 if (!cdesc) {
1303 SharedCategory *scat = find_custom_category (category);
1304 if (!scat)
1305 return NULL;
1306 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1308 return mono_string_new (mono_domain_get (), cdesc->help);
1312 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1313 * TRUE only if a counter with that name exists in the category.
1315 MonoBoolean
1316 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1318 const CategoryDesc *cdesc;
1319 /* no support for counters on other machines */
1320 if (mono_string_compare_ascii (machine, "."))
1321 return FALSE;
1322 cdesc = find_category (category);
1323 if (!cdesc) {
1324 SharedCategory *scat = find_custom_category (category);
1325 if (!scat)
1326 return FALSE;
1327 /* counter is allowed to be null */
1328 if (!counter)
1329 return TRUE;
1330 /* search through the custom category */
1331 return find_custom_counter (scat, counter) != NULL;
1333 /* counter is allowed to be null */
1334 if (!counter)
1335 return TRUE;
1336 if (get_counter_in_category (cdesc, counter))
1337 return TRUE;
1338 return FALSE;
1341 /* C map of the type with the same name */
1342 typedef struct {
1343 MonoObject object;
1344 MonoString *help;
1345 MonoString *name;
1346 int type;
1347 } CounterCreationData;
1350 * Since we'll keep a copy of the category per-process, we should also make sure
1351 * categories with the same name are compatible.
1353 MonoBoolean
1354 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1356 MonoError error;
1357 int result = FALSE;
1358 int i, size;
1359 int num_counters = mono_array_length (items);
1360 int counters_data_size;
1361 char *name = NULL;
1362 char *chelp = NULL;
1363 char **counter_info = NULL;
1364 unsigned char *ptr;
1365 char *p;
1366 SharedCategory *cat;
1368 /* FIXME: ensure there isn't a category created already */
1369 mono_error_init (&error);
1370 name = mono_string_to_utf8_checked (category, &error);
1371 if (!mono_error_ok (&error))
1372 goto failure;
1373 chelp = mono_string_to_utf8_checked (help, &error);
1374 if (!mono_error_ok (&error))
1375 goto failure;
1376 counter_info = g_new0 (char*, num_counters * 2);
1377 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1378 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1379 for (i = 0; i < num_counters; ++i) {
1380 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1381 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1382 if (!mono_error_ok (&error))
1383 goto failure;
1384 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1385 if (!mono_error_ok (&error))
1386 goto failure;
1387 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1389 for (i = 0; i < num_counters * 2; ++i) {
1390 if (!counter_info [i])
1391 goto failure;
1392 size += strlen (counter_info [i]) + 1;
1394 size += 7;
1395 size &= ~7;
1396 counters_data_size = num_counters * 8; /* optimize for size later */
1397 if (size > 65535)
1398 goto failure;
1399 perfctr_lock ();
1400 ptr = shared_data_find_room (size);
1401 if (!ptr) {
1402 perfctr_unlock ();
1403 goto failure;
1405 cat = (SharedCategory*)ptr;
1406 cat->header.extra = type;
1407 cat->header.size = size;
1408 cat->num_counters = num_counters;
1409 cat->counters_data_size = counters_data_size;
1410 /* now copy the vaiable data */
1411 p = cat->name;
1412 strcpy (p, name);
1413 p += strlen (name) + 1;
1414 strcpy (p, chelp);
1415 p += strlen (chelp) + 1;
1416 for (i = 0; i < num_counters; ++i) {
1417 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1418 /* emit the SharedCounter structures */
1419 *p++ = perfctr_type_compress (data->type);
1420 *p++ = i;
1421 strcpy (p, counter_info [i * 2]);
1422 p += strlen (counter_info [i * 2]) + 1;
1423 strcpy (p, counter_info [i * 2 + 1]);
1424 p += strlen (counter_info [i * 2 + 1]) + 1;
1426 cat->header.ftype = FTYPE_CATEGORY;
1428 perfctr_unlock ();
1429 result = TRUE;
1430 failure:
1431 if (counter_info) {
1432 for (i = 0; i < num_counters * 2; ++i) {
1433 g_free (counter_info [i]);
1435 g_free (counter_info);
1437 g_free (name);
1438 g_free (chelp);
1439 mono_error_cleanup (&error);
1440 return result;
1444 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1446 const CategoryDesc *cdesc;
1447 /* no support for counters on other machines */
1448 /*FIXME: machine appears to be wrong
1449 if (mono_string_compare_ascii (machine, "."))
1450 return FALSE;*/
1451 cdesc = find_category (category);
1452 if (!cdesc) {
1453 SharedCategory *scat;
1454 scat = find_custom_category (category);
1455 if (!scat)
1456 return FALSE;
1457 if (find_custom_instance (scat, instance))
1458 return TRUE;
1459 } else {
1460 /* FIXME: search instance */
1462 return FALSE;
1465 MonoArray*
1466 mono_perfcounter_category_names (MonoString *machine)
1468 int i;
1469 MonoArray *res;
1470 MonoDomain *domain = mono_domain_get ();
1471 GSList *custom_categories, *tmp;
1472 /* no support for counters on other machines */
1473 if (mono_string_compare_ascii (machine, "."))
1474 return mono_array_new (domain, mono_get_string_class (), 0);
1475 perfctr_lock ();
1476 custom_categories = get_custom_categories ();
1477 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1478 for (i = 0; i < NUM_CATEGORIES; ++i) {
1479 const CategoryDesc *cdesc = &predef_categories [i];
1480 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1482 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1483 SharedCategory *scat = tmp->data;
1484 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1485 i++;
1487 perfctr_unlock ();
1488 g_slist_free (custom_categories);
1489 return res;
1492 MonoArray*
1493 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1495 int i;
1496 SharedCategory *scat;
1497 const CategoryDesc *cdesc;
1498 MonoArray *res;
1499 MonoDomain *domain = mono_domain_get ();
1500 /* no support for counters on other machines */
1501 if (mono_string_compare_ascii (machine, "."))
1502 return mono_array_new (domain, mono_get_string_class (), 0);
1503 cdesc = find_category (category);
1504 if (cdesc) {
1505 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1506 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1507 const CounterDesc *desc = &predef_counters [i];
1508 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1510 return res;
1512 perfctr_lock ();
1513 scat = find_custom_category (category);
1514 if (scat) {
1515 char *p = custom_category_counters (scat);
1516 int i;
1517 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1518 for (i = 0; i < scat->num_counters; ++i) {
1519 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1520 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1521 p += strlen (p) + 1; /* skip counter help */
1523 perfctr_unlock ();
1524 return res;
1526 perfctr_unlock ();
1527 return mono_array_new (domain, mono_get_string_class (), 0);
1530 static MonoArray*
1531 get_string_array (void **array, int count, gboolean is_process)
1533 int i;
1534 MonoDomain *domain = mono_domain_get ();
1535 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1536 for (i = 0; i < count; ++i) {
1537 char buf [128];
1538 char *p;
1539 if (is_process) {
1540 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1541 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1542 } else {
1543 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1544 p = buf;
1546 mono_array_setref (res, i, mono_string_new (domain, p));
1547 if (p != buf)
1548 g_free (p);
1550 return res;
1553 static MonoArray*
1554 get_string_array_of_strings (void **array, int count)
1556 int i;
1557 MonoDomain *domain = mono_domain_get ();
1558 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1559 for (i = 0; i < count; ++i) {
1560 char* p = array[i];
1561 mono_array_setref (res, i, mono_string_new (domain, p));
1564 return res;
1567 static MonoArray*
1568 get_mono_instances (void)
1570 int count = 64;
1571 int res;
1572 void **buf = NULL;
1573 MonoArray *array;
1574 do {
1575 count *= 2;
1576 g_free (buf);
1577 buf = g_new (void*, count);
1578 res = mono_shared_area_instances (buf, count);
1579 } while (res == count);
1580 array = get_string_array (buf, res, TRUE);
1581 g_free (buf);
1582 return array;
1585 static MonoArray*
1586 get_cpu_instances (void)
1588 void **buf = NULL;
1589 int i, count;
1590 MonoArray *array;
1592 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1593 buf = g_new (void*, count);
1594 for (i = 0; i < count; ++i)
1595 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1596 array = get_string_array (buf, count, FALSE);
1597 g_free (buf);
1598 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1599 return array;
1602 static MonoArray*
1603 get_processes_instances (void)
1605 MonoArray *array;
1606 int count = 0;
1607 void **buf = mono_process_list (&count);
1608 if (!buf)
1609 return get_string_array (NULL, 0, FALSE);
1610 array = get_string_array (buf, count, TRUE);
1611 g_free (buf);
1612 return array;
1615 static MonoArray*
1616 get_networkinterface_instances (void)
1618 MonoArray *array;
1619 int count = 0;
1620 void **buf = mono_networkinterface_list (&count);
1621 if (!buf)
1622 return get_string_array_of_strings (NULL, 0);
1623 array = get_string_array_of_strings (buf, count);
1624 g_strfreev ((char **) buf);
1625 return array;
1628 static MonoArray*
1629 get_custom_instances (MonoString *category)
1631 SharedCategory *scat;
1632 scat = find_custom_category (category);
1633 if (scat) {
1634 GSList *list = get_custom_instances_list (scat);
1635 GSList *tmp;
1636 int i = 0;
1637 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1638 for (tmp = list; tmp; tmp = tmp->next) {
1639 SharedInstance *inst = tmp->data;
1640 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1641 i++;
1643 g_slist_free (list);
1644 return array;
1646 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1649 MonoArray*
1650 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1652 const CategoryDesc* cat;
1653 if (mono_string_compare_ascii (machine, "."))
1654 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1655 cat = find_category (category);
1656 if (!cat)
1657 return get_custom_instances (category);
1658 switch (cat->instance_type) {
1659 case MonoInstance:
1660 return get_mono_instances ();
1661 case CPUInstance:
1662 return get_cpu_instances ();
1663 case ProcessInstance:
1664 return get_processes_instances ();
1665 case NetworkInterfaceInstance:
1666 return get_networkinterface_instances ();
1667 case ThreadInstance:
1668 default:
1669 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);