2010-04-07 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / mono-perfcounters.c
blob6bd5fc032f2ed2c7cf17badc3b0ca967fec0ff9b
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_SYS_TIME_H
16 #include <sys/time.h>
17 #endif
18 #include "metadata/mono-perfcounters.h"
19 #include "metadata/appdomain.h"
20 #include "metadata/object-internals.h"
21 /* for mono_stats */
22 #include "metadata/class-internals.h"
23 #include "utils/mono-time.h"
24 #include "utils/mono-mmap.h"
25 #include "utils/mono-proclib.h"
26 #include "utils/mono-networkinterfaces.h"
27 #include "utils/mono-error-internals.h"
28 #include <mono/io-layer/io-layer.h>
30 /* map of CounterSample.cs */
31 struct _MonoCounterSample {
32 gint64 rawValue;
33 gint64 baseValue;
34 gint64 counterFrequency;
35 gint64 systemFrequency;
36 gint64 timeStamp;
37 gint64 timeStamp100nSec;
38 gint64 counterTimeStamp;
39 int counterType;
42 /* map of PerformanceCounterType.cs */
43 enum {
44 NumberOfItemsHEX32=0x00000000,
45 NumberOfItemsHEX64=0x00000100,
46 NumberOfItems32=0x00010000,
47 NumberOfItems64=0x00010100,
48 CounterDelta32=0x00400400,
49 CounterDelta64=0x00400500,
50 SampleCounter=0x00410400,
51 CountPerTimeInterval32=0x00450400,
52 CountPerTimeInterval64=0x00450500,
53 RateOfCountsPerSecond32=0x10410400,
54 RateOfCountsPerSecond64=0x10410500,
55 RawFraction=0x20020400,
56 CounterTimer=0x20410500,
57 Timer100Ns=0x20510500,
58 SampleFraction=0x20C20400,
59 CounterTimerInverse=0x21410500,
60 Timer100NsInverse=0x21510500,
61 CounterMultiTimer=0x22410500,
62 CounterMultiTimer100Ns=0x22510500,
63 CounterMultiTimerInverse=0x23410500,
64 CounterMultiTimer100NsInverse=0x23510500,
65 AverageTimer32=0x30020400,
66 ElapsedTime=0x30240500,
67 AverageCount64=0x40020500,
68 SampleBase=0x40030401,
69 AverageBase=0x40030402,
70 RawBase=0x40030403,
71 CounterMultiBase=0x42030500
74 /* maps a small integer type to the counter types above */
75 static const int
76 simple_type_to_type [] = {
77 NumberOfItemsHEX32, NumberOfItemsHEX64,
78 NumberOfItems32, NumberOfItems64,
79 CounterDelta32, CounterDelta64,
80 SampleCounter, CountPerTimeInterval32,
81 CountPerTimeInterval64, RateOfCountsPerSecond32,
82 RateOfCountsPerSecond64, RawFraction,
83 CounterTimer, Timer100Ns,
84 SampleFraction, CounterTimerInverse,
85 Timer100NsInverse, CounterMultiTimer,
86 CounterMultiTimer100Ns, CounterMultiTimerInverse,
87 CounterMultiTimer100NsInverse, AverageTimer32,
88 ElapsedTime, AverageCount64,
89 SampleBase, AverageBase,
90 RawBase, CounterMultiBase
93 enum {
94 SingleInstance,
95 MultiInstance,
96 CatTypeUnknown = -1
99 enum {
100 ProcessInstance,
101 ThreadInstance,
102 CPUInstance,
103 MonoInstance,
104 NetworkInterfaceInstance,
105 CustomInstance
108 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
109 #define PERFCTR_COUNTER(id,name,help,type,field)
110 enum {
111 #include "mono-perfcounters-def.h"
112 NUM_CATEGORIES
115 #undef PERFCTR_CAT
116 #undef PERFCTR_COUNTER
117 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
118 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
119 /* each counter is assigned an id starting from 0 inside the category */
120 enum {
121 #include "mono-perfcounters-def.h"
122 END_COUNTERS
125 #undef PERFCTR_CAT
126 #undef PERFCTR_COUNTER
127 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
128 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
129 /* this is used just to count the number of counters */
130 enum {
131 #include "mono-perfcounters-def.h"
132 NUM_COUNTERS
135 static CRITICAL_SECTION perfctr_mutex;
136 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
137 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
139 typedef struct {
140 char reserved [16];
141 int size;
142 unsigned short counters_start;
143 unsigned short counters_size;
144 unsigned short data_start;
145 MonoPerfCounters counters;
146 char data [1];
147 } MonoSharedArea;
150 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
151 basic stanza:
152 struct stanza_header {
153 byte stanza_type; // FTYPE_*
154 byte other_info;
155 ushort stanza_length; // includeas header
156 ... data ...
159 // strings are utf8
160 // perfcat and perfinstance are 4-bytes aligned
161 struct perfcat {
162 byte typeidx;
163 byte categorytype;
164 ushort length; // includes the counters
165 ushort num_counters;
166 ushort counters_data_size;
167 int num_instances;
168 char name[]; // null terminated
169 char help[]; // null terminated
170 // perfcounters follow
172 byte countertype;
173 char name[]; // null terminated
174 char help[]; // null terminated
176 0-byte
179 struct perfinstance {
180 byte typeidx;
181 byte data_offset; // offset of counters from beginning of struct
182 ushort length;
183 uint category_offset; // offset of category in the shared area
184 char name[]; // null terminated
185 // data follows: this is always 8-byte aligned
190 enum {
191 FTYPE_CATEGORY = 'C',
192 FTYPE_DELETED = 'D',
193 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
194 FTYPE_INSTANCE = 'I',
195 FTYPE_DIRTY = 'd',
196 FTYPE_END = 0
199 typedef struct {
200 unsigned char ftype;
201 unsigned char extra;
202 unsigned short size;
203 } SharedHeader;
205 typedef struct {
206 SharedHeader header;
207 unsigned short num_counters;
208 unsigned short counters_data_size;
209 int num_instances;
210 /* variable length data follows */
211 char name [1];
212 } SharedCategory;
214 typedef struct {
215 SharedHeader header;
216 unsigned int category_offset;
217 /* variable length data follows */
218 char instance_name [1];
219 } SharedInstance;
221 typedef struct {
222 unsigned char type;
223 guint8 seq_num;
224 /* variable length data follows */
225 char name [1];
226 } SharedCounter;
228 typedef struct {
229 const char *name;
230 const char *help;
231 unsigned char id;
232 signed int type : 2;
233 unsigned int instance_type : 6;
234 short first_counter;
235 } CategoryDesc;
237 typedef struct {
238 const char *name;
239 const char *help;
240 short id;
241 unsigned short offset; // offset inside MonoPerfCounters
242 int type;
243 } CounterDesc;
245 #undef PERFCTR_CAT
246 #undef PERFCTR_COUNTER
247 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
248 #define PERFCTR_COUNTER(id,name,help,type,field)
249 static const CategoryDesc
250 predef_categories [] = {
251 #include "mono-perfcounters-def.h"
252 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
255 #undef PERFCTR_CAT
256 #undef PERFCTR_COUNTER
257 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
258 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
259 static const CounterDesc
260 predef_counters [] = {
261 #include "mono-perfcounters-def.h"
262 {NULL, NULL, -1, 0, 0}
266 * We have several different classes of counters:
267 * *) system counters
268 * *) runtime counters
269 * *) remote counters
270 * *) user-defined counters
271 * *) windows counters (the implementation on windows will use this)
273 * To easily handle the differences we create a vtable for each class that contains the
274 * function pointers with the actual implementation to access the counters.
276 typedef struct _ImplVtable ImplVtable;
278 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
279 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
280 typedef void (*CleanupFunc) (ImplVtable *vtable);
282 struct _ImplVtable {
283 void *arg;
284 SampleFunc sample;
285 UpdateFunc update;
286 CleanupFunc cleanup;
289 typedef struct {
290 int id;
291 char *name;
292 } NetworkVtableArg;
294 typedef struct {
295 ImplVtable vtable;
296 MonoPerfCounters *counters;
297 int pid;
298 } PredefVtable;
300 typedef struct {
301 ImplVtable vtable;
302 SharedInstance *instance_desc;
303 SharedCounter *counter_desc;
304 } CustomVTable;
306 static ImplVtable*
307 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
309 ImplVtable *vtable = g_new0 (ImplVtable, 1);
310 vtable->arg = arg;
311 vtable->sample = sample;
312 vtable->update = update;
313 return vtable;
316 MonoPerfCounters *mono_perfcounters = NULL;
317 static MonoSharedArea *shared_area = NULL;
319 typedef struct {
320 void *sarea;
321 int refcount;
322 } ExternalSArea;
324 /* maps a pid to a ExternalSArea pointer */
325 static GHashTable *pid_to_shared_area = NULL;
327 static MonoSharedArea *
328 load_sarea_for_pid (int pid)
330 ExternalSArea *data;
331 MonoSharedArea *area = NULL;
333 perfctr_lock ();
334 if (pid_to_shared_area == NULL)
335 pid_to_shared_area = g_hash_table_new (NULL, NULL);
336 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
337 if (!data) {
338 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
339 if (area) {
340 data = g_new (ExternalSArea, 1);
341 data->sarea = area;
342 data->refcount = 1;
343 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
345 } else {
346 area = data->sarea;
347 data->refcount ++;
349 perfctr_unlock ();
350 return area;
353 static void
354 unref_pid_unlocked (int pid)
356 ExternalSArea *data;
357 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
358 if (data) {
359 data->refcount--;
360 if (!data->refcount) {
361 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
362 mono_shared_area_unload (data->sarea);
363 g_free (data);
368 static void
369 predef_cleanup (ImplVtable *vtable)
371 PredefVtable *vt = (PredefVtable*)vtable;
372 /* ExternalSArea *data; */
374 perfctr_lock ();
375 if (!pid_to_shared_area) {
376 perfctr_unlock ();
377 return;
379 unref_pid_unlocked (vt->pid);
380 perfctr_unlock ();
383 void
384 mono_perfcounters_init (void)
386 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
387 d_offset += 7;
388 d_offset &= ~7;
390 InitializeCriticalSection (&perfctr_mutex);
392 shared_area = mono_shared_area ();
393 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
394 shared_area->counters_size = sizeof (MonoPerfCounters);
395 shared_area->data_start = d_offset;
396 shared_area->size = 4096;
397 mono_perfcounters = &shared_area->counters;
400 static int
401 perfctr_type_compress (int type)
403 int i;
404 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
405 if (simple_type_to_type [i] == type)
406 return i;
408 /* NumberOfItems32 */
409 return 2;
412 static unsigned char*
413 shared_data_find_room (int size)
415 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
416 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
418 size += 7;
419 size &= ~7;
420 while (p < end) {
421 unsigned short *next;
422 if (*p == FTYPE_END) {
423 if (size < (end - p))
424 return p;
425 return NULL;
427 if (p + 4 > end)
428 return NULL;
429 next = (unsigned short*)(p + 2);
430 if (*p == FTYPE_DELETED) {
431 /* we reuse only if it's the same size */
432 if (*next == size) {
433 return p;
436 p += *next;
438 return NULL;
441 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
443 static void
444 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
446 while (p < end) {
447 unsigned short *next;
448 if (p + 4 > end)
449 return;
450 next = (unsigned short*)(p + 2);
451 if (!func ((SharedHeader*)p, data))
452 return;
453 if (*p == FTYPE_END)
454 return;
455 p += *next;
459 static void
460 foreach_shared_item (SharedFunc func, void *data)
462 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
463 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
465 foreach_shared_item_in_area (p, end, func, data);
468 static int
469 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
471 /* FIXME: make this case insensitive */
472 guint16 *strc = mono_string_chars (str);
473 while (*strc == *ascii_str++) {
474 if (*strc == 0)
475 return 0;
476 strc++;
478 return *strc - *(const unsigned char *)(ascii_str - 1);
481 typedef struct {
482 MonoString *name;
483 SharedCategory *cat;
484 } CatSearch;
486 static gboolean
487 category_search (SharedHeader *header, void *data)
489 CatSearch *search = data;
490 if (header->ftype == FTYPE_CATEGORY) {
491 SharedCategory *cat = (SharedCategory*)header;
492 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
493 search->cat = cat;
494 return FALSE;
497 return TRUE;
500 static SharedCategory*
501 find_custom_category (MonoString *name)
503 CatSearch search;
504 search.name = name;
505 search.cat = NULL;
506 foreach_shared_item (category_search, &search);
507 return search.cat;
510 static gboolean
511 category_collect (SharedHeader *header, void *data)
513 GSList **list = data;
514 if (header->ftype == FTYPE_CATEGORY) {
515 *list = g_slist_prepend (*list, header);
517 return TRUE;
520 static GSList*
521 get_custom_categories (void) {
522 GSList *list = NULL;
523 foreach_shared_item (category_collect, &list);
524 return list;
527 static char*
528 custom_category_counters (SharedCategory* cat)
530 char *p = cat->name + strlen (cat->name) + 1;
531 p += strlen (p) + 1; /* skip category help */
532 return p;
535 static SharedCounter*
536 find_custom_counter (SharedCategory* cat, MonoString *name)
538 int i;
539 char *p = custom_category_counters (cat);
540 for (i = 0; i < cat->num_counters; ++i) {
541 SharedCounter *counter = (SharedCounter*)p;
542 if (mono_string_compare_ascii (name, counter->name) == 0)
543 return counter;
544 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
545 p += strlen (p) + 1; /* skip counter help */
547 return NULL;
550 typedef struct {
551 unsigned int cat_offset;
552 SharedCategory* cat;
553 MonoString *instance;
554 SharedInstance* result;
555 GSList *list;
556 } InstanceSearch;
558 static gboolean
559 instance_search (SharedHeader *header, void *data)
561 InstanceSearch *search = data;
562 if (header->ftype == FTYPE_INSTANCE) {
563 SharedInstance *ins = (SharedInstance*)header;
564 if (search->cat_offset == ins->category_offset) {
565 if (search->instance) {
566 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
567 search->result = ins;
568 return FALSE;
570 } else {
571 search->list = g_slist_prepend (search->list, ins);
575 return TRUE;
578 static SharedInstance*
579 find_custom_instance (SharedCategory* cat, MonoString *instance)
581 InstanceSearch search;
582 search.cat_offset = (char*)cat - (char*)shared_area;
583 search.cat = cat;
584 search.instance = instance;
585 search.list = NULL;
586 search.result = NULL;
587 foreach_shared_item (instance_search, &search);
588 return search.result;
591 static GSList*
592 get_custom_instances_list (SharedCategory* cat)
594 InstanceSearch search;
595 search.cat_offset = (char*)cat - (char*)shared_area;
596 search.cat = cat;
597 search.instance = NULL;
598 search.list = NULL;
599 search.result = NULL;
600 foreach_shared_item (instance_search, &search);
601 return search.list;
604 static char*
605 custom_category_help (SharedCategory* cat)
607 return cat->name + strlen (cat->name) + 1;
610 static const CounterDesc*
611 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
613 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
614 const CounterDesc *end = &predef_counters [desc [1].first_counter];
615 for (; cdesc < end; ++cdesc) {
616 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
617 return cdesc;
619 return NULL;
622 /* fill the info in sample (except the raw value) */
623 static void
624 fill_sample (MonoCounterSample *sample)
626 sample->timeStamp = mono_100ns_ticks ();
627 sample->timeStamp100nSec = sample->timeStamp;
628 sample->counterTimeStamp = sample->timeStamp;
629 sample->counterFrequency = 10000000;
630 sample->systemFrequency = 10000000;
631 // the real basevalue needs to be get from a different counter...
632 sample->baseValue = 0;
635 static int
636 id_from_string (MonoString *instance, gboolean is_process)
638 int id = -1;
639 if (mono_string_length (instance)) {
640 char *id_str = mono_string_to_utf8 (instance);
641 char *end;
642 id = strtol (id_str, &end, 0);
643 if (end == id_str && !is_process)
644 id = -1;
645 g_free (id_str);
647 return id;
650 static MonoBoolean
651 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
653 MonoProcessError error;
654 int id = GPOINTER_TO_INT (vtable->arg);
655 int pid = id >> 5;
656 id &= 0x1f;
657 if (!only_value) {
658 fill_sample (sample);
659 sample->baseValue = 1;
661 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
662 switch (id) {
663 case COUNTER_CPU_USER_TIME:
664 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
665 return TRUE;
666 case COUNTER_CPU_PRIV_TIME:
667 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
668 return TRUE;
669 case COUNTER_CPU_INTR_TIME:
670 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
671 return TRUE;
672 case COUNTER_CPU_DCP_TIME:
673 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
674 return TRUE;
675 case COUNTER_CPU_PROC_TIME:
676 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
677 return TRUE;
679 return FALSE;
682 static void*
683 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
685 int id = id_from_string (instance, FALSE) << 5;
686 const CounterDesc *cdesc;
687 *custom = FALSE;
688 /* increase the shift above and the mask also in the implementation functions */
689 //g_assert (32 > desc [1].first_counter - desc->first_counter);
690 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
691 *type = cdesc->type;
692 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
694 return NULL;
697 static MonoBoolean
698 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
700 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
701 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
702 if (!only_value) {
703 fill_sample (sample);
706 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
707 switch (narg->id) {
708 case COUNTER_NETWORK_BYTESRECSEC:
709 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
710 break;
711 case COUNTER_NETWORK_BYTESSENTSEC:
712 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
713 break;
714 case COUNTER_NETWORK_BYTESTOTALSEC:
715 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
716 break;
719 if (error == MONO_NETWORK_ERROR_NONE)
720 return TRUE;
721 else
722 return FALSE;
725 static void
726 network_cleanup (ImplVtable *vtable)
728 NetworkVtableArg *narg;
730 if (vtable == NULL)
731 return;
733 narg = vtable->arg;
734 if (narg == NULL)
735 return;
737 g_free (narg->name);
738 narg->name = NULL;
739 g_free (narg);
740 vtable->arg = NULL;
743 static void*
744 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
746 const CounterDesc *cdesc;
747 NetworkVtableArg *narg;
748 ImplVtable *vtable;
749 char *instance_name;
751 *custom = FALSE;
752 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
753 instance_name = mono_string_to_utf8 (instance);
754 narg = g_new0 (NetworkVtableArg, 1);
755 narg->id = cdesc->id;
756 narg->name = instance_name;
757 *type = cdesc->type;
758 vtable = create_vtable (narg, get_network_counter, NULL);
759 vtable->cleanup = network_cleanup;
760 return vtable;
762 return NULL;
765 static MonoBoolean
766 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
768 int id = GPOINTER_TO_INT (vtable->arg);
769 int pid = id >> 5;
770 if (pid < 0)
771 return FALSE;
772 id &= 0x1f;
773 if (!only_value) {
774 fill_sample (sample);
775 sample->baseValue = 1;
777 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
778 switch (id) {
779 case COUNTER_PROC_USER_TIME:
780 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
781 return TRUE;
782 case COUNTER_PROC_PRIV_TIME:
783 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
784 return TRUE;
785 case COUNTER_PROC_PROC_TIME:
786 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
787 return TRUE;
788 case COUNTER_PROC_THREADS:
789 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
790 return TRUE;
791 case COUNTER_PROC_VBYTES:
792 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
793 return TRUE;
794 case COUNTER_PROC_WSET:
795 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
796 return TRUE;
797 case COUNTER_PROC_PBYTES:
798 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
799 return TRUE;
801 return FALSE;
804 static void*
805 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
807 int id = id_from_string (instance, TRUE) << 5;
808 const CounterDesc *cdesc;
809 *custom = FALSE;
810 /* increase the shift above and the mask also in the implementation functions */
811 //g_assert (32 > desc [1].first_counter - desc->first_counter);
812 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
813 *type = cdesc->type;
814 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
816 return NULL;
819 static MonoBoolean
820 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
822 int id = GPOINTER_TO_INT (vtable->arg);
823 if (!only_value) {
824 fill_sample (sample);
825 sample->baseValue = 1;
827 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
828 switch (id) {
829 case COUNTER_MEM_NUM_OBJECTS:
830 sample->rawValue = mono_stats.new_object_count;
831 return TRUE;
833 return FALSE;
836 static void*
837 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
839 const CounterDesc *cdesc;
840 *custom = FALSE;
841 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
842 *type = cdesc->type;
843 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
845 return NULL;
848 static MonoBoolean
849 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
851 PredefVtable *vt = (PredefVtable *)vtable;
852 const CounterDesc *desc;
853 int cat_id = GPOINTER_TO_INT (vtable->arg);
854 int id = cat_id >> 16;
855 cat_id &= 0xffff;
856 if (!only_value) {
857 fill_sample (sample);
858 sample->baseValue = 1;
860 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
861 sample->counterType = desc->type;
862 /* FIXME: check that the offset fits inside imported counters */
863 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
864 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
865 return TRUE;
868 static ImplVtable*
869 predef_vtable (void *arg, MonoString *instance)
871 MonoSharedArea *area;
872 PredefVtable *vtable;
873 char *pids = mono_string_to_utf8 (instance);
874 int pid;
876 pid = atoi (pids);
877 g_free (pids);
878 area = load_sarea_for_pid (pid);
879 if (!area)
880 return NULL;
882 vtable = g_new (PredefVtable, 1);
883 vtable->vtable.arg = arg;
884 vtable->vtable.sample = predef_readonly_counter;
885 vtable->vtable.cleanup = predef_cleanup;
886 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
887 vtable->pid = pid;
889 return (ImplVtable*)vtable;
892 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
893 * this needs some way to set sample->counterType as well, though.
895 static MonoBoolean
896 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
898 int cat_id = GPOINTER_TO_INT (vtable->arg);
899 int id = cat_id >> 16;
900 cat_id &= 0xffff;
901 if (!only_value) {
902 fill_sample (sample);
903 sample->baseValue = 1;
905 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
906 switch (cat_id) {
907 case CATEGORY_EXC:
908 switch (id) {
909 case COUNTER_EXC_THROWN:
910 sample->rawValue = mono_perfcounters->exceptions_thrown;
911 return TRUE;
913 break;
914 case CATEGORY_ASPNET:
915 switch (id) {
916 case COUNTER_ASPNET_REQ_Q:
917 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
918 return TRUE;
919 case COUNTER_ASPNET_REQ_TOTAL:
920 sample->rawValue = mono_perfcounters->aspnet_requests;
921 return TRUE;
923 break;
924 case CATEGORY_THREADPOOL:
925 switch (id) {
926 case COUNTER_THREADPOOL_WORKITEMS:
927 sample->rawValue = mono_perfcounters->threadpool_workitems;
928 return TRUE;
929 case COUNTER_THREADPOOL_IOWORKITEMS:
930 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
931 return TRUE;
932 case COUNTER_THREADPOOL_THREADS:
933 sample->rawValue = mono_perfcounters->threadpool_threads;
934 return TRUE;
935 case COUNTER_THREADPOOL_IOTHREADS:
936 sample->rawValue = mono_perfcounters->threadpool_iothreads;
937 return TRUE;
939 break;
941 return FALSE;
944 static gint64
945 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
947 guint32 *volatile ptr = NULL;
948 gint64 *volatile ptr64 = NULL;
949 int cat_id = GPOINTER_TO_INT (vtable->arg);
950 int id = cat_id >> 16;
951 cat_id &= 0xffff;
952 switch (cat_id) {
953 case CATEGORY_ASPNET:
954 switch (id) {
955 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
956 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
958 break;
959 case CATEGORY_THREADPOOL:
960 switch (id) {
961 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
962 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
963 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
964 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
966 break;
968 if (ptr) {
969 if (do_incr) {
970 if (value == 1)
971 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
972 if (value == -1)
973 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
975 *ptr += value;
976 return *ptr;
978 /* this can be non-atomic */
979 *ptr = value;
980 return value;
981 } else if (ptr64) {
982 if (do_incr) {
983 /* FIXME: we need to do this atomically */
984 /* No InterlockedIncrement64() yet */
986 if (value == 1)
987 return InterlockedIncrement64 (ptr);
988 if (value == -1)
989 return InterlockedDecrement64 (ptr);
992 *ptr64 += value;
993 return *ptr64;
995 /* this can be non-atomic */
996 *ptr64 = value;
997 return value;
999 return 0;
1002 static void*
1003 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1005 const CounterDesc *cdesc;
1006 *custom = TRUE;
1007 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1008 *type = cdesc->type;
1009 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1010 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1011 else
1012 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1014 return NULL;
1017 static MonoBoolean
1018 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1020 CustomVTable *counter_data = (CustomVTable *)vtable;
1021 if (!only_value) {
1022 fill_sample (sample);
1023 sample->baseValue = 1;
1025 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1026 if (!vtable->arg)
1027 sample->rawValue = 0;
1028 else
1029 sample->rawValue = *(guint64*)vtable->arg;
1030 return TRUE;
1033 static gint64
1034 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1036 /* FIXME: check writability */
1037 guint64 *ptr = vtable->arg;
1038 if (ptr) {
1039 if (do_incr) {
1040 /* FIXME: we need to do this atomically */
1041 *ptr += value;
1042 return *ptr;
1044 /* this can be non-atomic */
1045 *ptr = value;
1046 return value;
1048 return 0;
1051 static SharedInstance*
1052 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
1054 SharedInstance* inst;
1055 unsigned char *ptr;
1056 char *p;
1057 int size, data_offset;
1058 char *name;
1059 inst = find_custom_instance (cat, instance);
1060 if (inst)
1061 return inst;
1062 name = mono_string_to_utf8 (instance);
1063 size = sizeof (SharedInstance) + strlen (name);
1064 size += 7;
1065 size &= ~7;
1066 data_offset = size;
1067 size += (sizeof (guint64) * cat->num_counters);
1068 perfctr_lock ();
1069 ptr = shared_data_find_room (size);
1070 if (!ptr) {
1071 perfctr_unlock ();
1072 g_free (name);
1073 return NULL;
1075 inst = (SharedInstance*)ptr;
1076 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
1077 inst->header.size = size;
1078 inst->category_offset = (char*)cat - (char*)shared_area;
1079 cat->num_instances++;
1080 /* now copy the variable data */
1081 p = inst->instance_name;
1082 strcpy (p, name);
1083 p += strlen (name) + 1;
1084 inst->header.ftype = FTYPE_INSTANCE;
1085 perfctr_unlock ();
1086 g_free (name);
1088 return inst;
1091 static ImplVtable*
1092 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1094 CustomVTable* vtable;
1095 vtable = g_new0 (CustomVTable, 1);
1096 vtable->vtable.arg = data;
1097 vtable->vtable.sample = custom_writable_counter;
1098 vtable->vtable.update = custom_writable_update;
1099 vtable->instance_desc = inst;
1100 vtable->counter_desc = scounter;
1102 return (ImplVtable*)vtable;
1105 static void*
1106 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1108 SharedCounter *scounter;
1109 SharedInstance* inst;
1110 int size;
1112 scounter = find_custom_counter (cat, counter);
1113 if (!scounter)
1114 return NULL;
1115 *type = simple_type_to_type [scounter->type];
1116 inst = custom_get_instance (cat, scounter, instance);
1117 if (!inst)
1118 return NULL;
1119 size = sizeof (SharedInstance) + strlen (inst->instance_name);
1120 size += 7;
1121 size &= ~7;
1122 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1125 static const CategoryDesc*
1126 find_category (MonoString *category)
1128 int i;
1129 for (i = 0; i < NUM_CATEGORIES; ++i) {
1130 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1131 return &predef_categories [i];
1133 return NULL;
1136 void*
1137 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1138 MonoString* machine, int *type, MonoBoolean *custom)
1140 const CategoryDesc *cdesc;
1141 /* no support for counters on other machines */
1142 if (mono_string_compare_ascii (machine, "."))
1143 return NULL;
1144 cdesc = find_category (category);
1145 if (!cdesc) {
1146 SharedCategory *scat = find_custom_category (category);
1147 if (!scat)
1148 return NULL;
1149 *custom = TRUE;
1150 return custom_get_impl (scat, counter, instance, type);
1152 switch (cdesc->id) {
1153 case CATEGORY_CPU:
1154 return cpu_get_impl (counter, instance, type, custom);
1155 case CATEGORY_PROC:
1156 return process_get_impl (counter, instance, type, custom);
1157 case CATEGORY_MONO_MEM:
1158 return mono_mem_get_impl (counter, instance, type, custom);
1159 case CATEGORY_NETWORK:
1160 return network_get_impl (counter, instance, type, custom);
1161 case CATEGORY_JIT:
1162 case CATEGORY_EXC:
1163 case CATEGORY_GC:
1164 case CATEGORY_REMOTING:
1165 case CATEGORY_LOADING:
1166 case CATEGORY_THREAD:
1167 case CATEGORY_INTEROP:
1168 case CATEGORY_SECURITY:
1169 case CATEGORY_ASPNET:
1170 case CATEGORY_THREADPOOL:
1171 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1173 return NULL;
1176 MonoBoolean
1177 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1179 ImplVtable *vtable = impl;
1180 if (vtable && vtable->sample)
1181 return vtable->sample (vtable, only_value, sample);
1182 return FALSE;
1185 gint64
1186 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1188 ImplVtable *vtable = impl;
1189 if (vtable && vtable->update)
1190 return vtable->update (vtable, do_incr, value);
1191 return 0;
1194 void
1195 mono_perfcounter_free_data (void *impl)
1197 ImplVtable *vtable = impl;
1198 if (vtable && vtable->cleanup)
1199 vtable->cleanup (vtable);
1200 g_free (impl);
1203 /* Category icalls */
1204 MonoBoolean
1205 mono_perfcounter_category_del (MonoString *name)
1207 const CategoryDesc *cdesc;
1208 SharedCategory *cat;
1209 cdesc = find_category (name);
1210 /* can't delete a predefined category */
1211 if (cdesc)
1212 return FALSE;
1213 perfctr_lock ();
1214 cat = find_custom_category (name);
1215 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1216 if (!cat || cat->num_instances) {
1217 perfctr_unlock ();
1218 return FALSE;
1220 cat->header.ftype = FTYPE_DELETED;
1221 perfctr_unlock ();
1222 return TRUE;
1225 MonoString*
1226 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1228 const CategoryDesc *cdesc;
1229 /* no support for counters on other machines */
1230 if (mono_string_compare_ascii (machine, "."))
1231 return NULL;
1232 cdesc = find_category (category);
1233 if (!cdesc) {
1234 SharedCategory *scat = find_custom_category (category);
1235 if (!scat)
1236 return NULL;
1237 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1239 return mono_string_new (mono_domain_get (), cdesc->help);
1243 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1244 * TRUE only if a counter with that name exists in the category.
1246 MonoBoolean
1247 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1249 const CategoryDesc *cdesc;
1250 /* no support for counters on other machines */
1251 if (mono_string_compare_ascii (machine, "."))
1252 return FALSE;
1253 cdesc = find_category (category);
1254 if (!cdesc) {
1255 SharedCategory *scat = find_custom_category (category);
1256 if (!scat)
1257 return FALSE;
1258 /* counter is allowed to be null */
1259 if (!counter)
1260 return TRUE;
1261 /* search through the custom category */
1262 return find_custom_counter (scat, counter) != NULL;
1264 /* counter is allowed to be null */
1265 if (!counter)
1266 return TRUE;
1267 if (get_counter_in_category (cdesc, counter))
1268 return TRUE;
1269 return FALSE;
1272 /* C map of the type with the same name */
1273 typedef struct {
1274 MonoObject object;
1275 MonoString *help;
1276 MonoString *name;
1277 int type;
1278 } CounterCreationData;
1281 * Since we'll keep a copy of the category per-process, we should also make sure
1282 * categories with the same name are compatible.
1284 MonoBoolean
1285 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1287 MonoError error;
1288 int result = FALSE;
1289 int i, size;
1290 int num_counters = mono_array_length (items);
1291 int counters_data_size;
1292 char *name = NULL;
1293 char *chelp = NULL;
1294 char **counter_info = NULL;
1295 unsigned char *ptr;
1296 char *p;
1297 SharedCategory *cat;
1299 /* FIXME: ensure there isn't a category created already */
1300 mono_error_init (&error);
1301 name = mono_string_to_utf8_checked (category, &error);
1302 if (!mono_error_ok (&error))
1303 goto failure;
1304 chelp = mono_string_to_utf8_checked (help, &error);
1305 if (!mono_error_ok (&error))
1306 goto failure;
1307 counter_info = g_new0 (char*, num_counters * 2);
1308 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1309 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1310 for (i = 0; i < num_counters; ++i) {
1311 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1312 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1313 if (!mono_error_ok (&error))
1314 goto failure;
1315 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1316 if (!mono_error_ok (&error))
1317 goto failure;
1318 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1320 for (i = 0; i < num_counters * 2; ++i) {
1321 if (!counter_info [i])
1322 goto failure;
1323 size += strlen (counter_info [i]) + 1;
1325 size += 7;
1326 size &= ~7;
1327 counters_data_size = num_counters * 8; /* optimize for size later */
1328 if (size > 65535)
1329 goto failure;
1330 perfctr_lock ();
1331 ptr = shared_data_find_room (size);
1332 if (!ptr) {
1333 perfctr_unlock ();
1334 goto failure;
1336 cat = (SharedCategory*)ptr;
1337 cat->header.extra = type;
1338 cat->header.size = size;
1339 cat->num_counters = num_counters;
1340 cat->counters_data_size = counters_data_size;
1341 /* now copy the vaiable data */
1342 p = cat->name;
1343 strcpy (p, name);
1344 p += strlen (name) + 1;
1345 strcpy (p, chelp);
1346 p += strlen (chelp) + 1;
1347 for (i = 0; i < num_counters; ++i) {
1348 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1349 /* emit the SharedCounter structures */
1350 *p++ = perfctr_type_compress (data->type);
1351 *p++ = i;
1352 strcpy (p, counter_info [i * 2]);
1353 p += strlen (counter_info [i * 2]) + 1;
1354 strcpy (p, counter_info [i * 2 + 1]);
1355 p += strlen (counter_info [i * 2 + 1]) + 1;
1357 cat->header.ftype = FTYPE_CATEGORY;
1359 perfctr_unlock ();
1360 result = TRUE;
1361 failure:
1362 if (counter_info) {
1363 for (i = 0; i < num_counters * 2; ++i) {
1364 g_free (counter_info [i]);
1366 g_free (counter_info);
1368 g_free (name);
1369 g_free (chelp);
1370 mono_error_cleanup (&error);
1371 return result;
1375 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1377 const CategoryDesc *cdesc;
1378 /* no support for counters on other machines */
1379 /*FIXME: machine appears to be wrong
1380 if (mono_string_compare_ascii (machine, "."))
1381 return FALSE;*/
1382 cdesc = find_category (category);
1383 if (!cdesc) {
1384 SharedCategory *scat;
1385 scat = find_custom_category (category);
1386 if (!scat)
1387 return FALSE;
1388 if (find_custom_instance (scat, instance))
1389 return TRUE;
1390 } else {
1391 /* FIXME: search instance */
1393 return FALSE;
1396 MonoArray*
1397 mono_perfcounter_category_names (MonoString *machine)
1399 int i;
1400 MonoArray *res;
1401 MonoDomain *domain = mono_domain_get ();
1402 GSList *custom_categories, *tmp;
1403 /* no support for counters on other machines */
1404 if (mono_string_compare_ascii (machine, "."))
1405 return mono_array_new (domain, mono_get_string_class (), 0);
1406 perfctr_lock ();
1407 custom_categories = get_custom_categories ();
1408 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1409 for (i = 0; i < NUM_CATEGORIES; ++i) {
1410 const CategoryDesc *cdesc = &predef_categories [i];
1411 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1413 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1414 SharedCategory *scat = tmp->data;
1415 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1416 i++;
1418 perfctr_unlock ();
1419 g_slist_free (custom_categories);
1420 return res;
1423 MonoArray*
1424 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1426 int i;
1427 SharedCategory *scat;
1428 const CategoryDesc *cdesc;
1429 MonoArray *res;
1430 MonoDomain *domain = mono_domain_get ();
1431 /* no support for counters on other machines */
1432 if (mono_string_compare_ascii (machine, "."))
1433 return mono_array_new (domain, mono_get_string_class (), 0);
1434 cdesc = find_category (category);
1435 if (cdesc) {
1436 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1437 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1438 const CounterDesc *desc = &predef_counters [i];
1439 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1441 return res;
1443 perfctr_lock ();
1444 scat = find_custom_category (category);
1445 if (scat) {
1446 char *p = custom_category_counters (scat);
1447 int i;
1448 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1449 for (i = 0; i < scat->num_counters; ++i) {
1450 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1451 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1452 p += strlen (p) + 1; /* skip counter help */
1454 perfctr_unlock ();
1455 return res;
1457 perfctr_unlock ();
1458 return mono_array_new (domain, mono_get_string_class (), 0);
1461 static MonoArray*
1462 get_string_array (void **array, int count, gboolean is_process)
1464 int i;
1465 MonoDomain *domain = mono_domain_get ();
1466 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1467 for (i = 0; i < count; ++i) {
1468 char buf [128];
1469 char *p;
1470 if (is_process) {
1471 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1472 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1473 } else {
1474 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1475 p = buf;
1477 mono_array_setref (res, i, mono_string_new (domain, p));
1478 if (p != buf)
1479 g_free (p);
1481 return res;
1484 static MonoArray*
1485 get_string_array_of_strings (void **array, int count)
1487 int i;
1488 MonoDomain *domain = mono_domain_get ();
1489 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1490 for (i = 0; i < count; ++i) {
1491 char* p = array[i];
1492 mono_array_setref (res, i, mono_string_new (domain, p));
1495 return res;
1498 static MonoArray*
1499 get_mono_instances (void)
1501 int count = 64;
1502 int res;
1503 void **buf = NULL;
1504 MonoArray *array;
1505 do {
1506 count *= 2;
1507 g_free (buf);
1508 buf = g_new (void*, count);
1509 res = mono_shared_area_instances (buf, count);
1510 } while (res == count);
1511 array = get_string_array (buf, res, TRUE);
1512 g_free (buf);
1513 return array;
1516 static MonoArray*
1517 get_cpu_instances (void)
1519 void **buf = NULL;
1520 int i, count;
1521 MonoArray *array;
1523 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1524 buf = g_new (void*, count);
1525 for (i = 0; i < count; ++i)
1526 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1527 array = get_string_array (buf, count, FALSE);
1528 g_free (buf);
1529 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1530 return array;
1533 static MonoArray*
1534 get_processes_instances (void)
1536 MonoArray *array;
1537 int count = 0;
1538 void **buf = mono_process_list (&count);
1539 if (!buf)
1540 return get_string_array (NULL, 0, FALSE);
1541 array = get_string_array (buf, count, TRUE);
1542 g_free (buf);
1543 return array;
1546 static MonoArray*
1547 get_networkinterface_instances (void)
1549 MonoArray *array;
1550 int count = 0;
1551 void **buf = mono_networkinterface_list (&count);
1552 if (!buf)
1553 return get_string_array_of_strings (NULL, 0);
1554 array = get_string_array_of_strings (buf, count);
1555 g_strfreev ((char **) buf);
1556 return array;
1559 static MonoArray*
1560 get_custom_instances (MonoString *category)
1562 SharedCategory *scat;
1563 scat = find_custom_category (category);
1564 if (scat) {
1565 GSList *list = get_custom_instances_list (scat);
1566 GSList *tmp;
1567 int i = 0;
1568 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1569 for (tmp = list; tmp; tmp = tmp->next) {
1570 SharedInstance *inst = tmp->data;
1571 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1572 i++;
1574 g_slist_free (list);
1575 return array;
1577 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1580 MonoArray*
1581 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1583 const CategoryDesc* cat;
1584 if (mono_string_compare_ascii (machine, "."))
1585 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1586 cat = find_category (category);
1587 if (!cat)
1588 return get_custom_instances (category);
1589 switch (cat->instance_type) {
1590 case MonoInstance:
1591 return get_mono_instances ();
1592 case CPUInstance:
1593 return get_cpu_instances ();
1594 case ProcessInstance:
1595 return get_processes_instances ();
1596 case NetworkInterfaceInstance:
1597 return get_networkinterface_instances ();
1598 case ThreadInstance:
1599 default:
1600 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);