4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
15 #ifdef HAVE_SYS_TIME_H
18 #include "metadata/mono-perfcounters.h"
19 #include "metadata/appdomain.h"
21 #include "metadata/class-internals.h"
22 #include "utils/mono-time.h"
23 #include "utils/mono-mmap.h"
24 #include "utils/mono-proclib.h"
25 #include <mono/io-layer/io-layer.h>
27 /* map of CounterSample.cs */
28 struct _MonoCounterSample {
31 gint64 counterFrequency;
32 gint64 systemFrequency;
34 gint64 timeStamp100nSec;
35 gint64 counterTimeStamp;
39 /* map of PerformanceCounterType.cs */
41 NumberOfItemsHEX32=0x00000000,
42 NumberOfItemsHEX64=0x00000100,
43 NumberOfItems32=0x00010000,
44 NumberOfItems64=0x00010100,
45 CounterDelta32=0x00400400,
46 CounterDelta64=0x00400500,
47 SampleCounter=0x00410400,
48 CountPerTimeInterval32=0x00450400,
49 CountPerTimeInterval64=0x00450500,
50 RateOfCountsPerSecond32=0x10410400,
51 RateOfCountsPerSecond64=0x10410500,
52 RawFraction=0x20020400,
53 CounterTimer=0x20410500,
54 Timer100Ns=0x20510500,
55 SampleFraction=0x20C20400,
56 CounterTimerInverse=0x21410500,
57 Timer100NsInverse=0x21510500,
58 CounterMultiTimer=0x22410500,
59 CounterMultiTimer100Ns=0x22510500,
60 CounterMultiTimerInverse=0x23410500,
61 CounterMultiTimer100NsInverse=0x23510500,
62 AverageTimer32=0x30020400,
63 ElapsedTime=0x30240500,
64 AverageCount64=0x40020500,
65 SampleBase=0x40030401,
66 AverageBase=0x40030402,
68 CounterMultiBase=0x42030500
71 /* maps a small integer type to the counter types above */
73 simple_type_to_type [] = {
74 NumberOfItemsHEX32, NumberOfItemsHEX64,
75 NumberOfItems32, NumberOfItems64,
76 CounterDelta32, CounterDelta64,
77 SampleCounter, CountPerTimeInterval32,
78 CountPerTimeInterval64, RateOfCountsPerSecond32,
79 RateOfCountsPerSecond64, RawFraction,
80 CounterTimer, Timer100Ns,
81 SampleFraction, CounterTimerInverse,
82 Timer100NsInverse, CounterMultiTimer,
83 CounterMultiTimer100Ns, CounterMultiTimerInverse,
84 CounterMultiTimer100NsInverse, AverageTimer32,
85 ElapsedTime, AverageCount64,
86 SampleBase, AverageBase,
87 RawBase, CounterMultiBase
104 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
105 #define PERFCTR_COUNTER(id,name,help,type,field)
107 #include "mono-perfcounters-def.h"
112 #undef PERFCTR_COUNTER
113 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
114 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
115 /* each counter is assigned an id starting from 0 inside the category */
117 #include "mono-perfcounters-def.h"
122 #undef PERFCTR_COUNTER
123 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
124 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
125 /* this is used just to count the number of counters */
127 #include "mono-perfcounters-def.h"
131 static CRITICAL_SECTION perfctr_mutex;
132 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
133 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
138 unsigned short counters_start;
139 unsigned short counters_size;
140 unsigned short data_start;
141 MonoPerfCounters counters;
146 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
148 struct stanza_header {
149 byte stanza_type; // FTYPE_*
151 ushort stanza_length; // includeas header
156 // perfcat and perfinstance are 4-bytes aligned
160 ushort length; // includes the counters
162 ushort counters_data_size;
164 char name[]; // null terminated
165 char help[]; // null terminated
166 // perfcounters follow
169 char name[]; // null terminated
170 char help[]; // null terminated
175 struct perfinstance {
177 byte data_offset; // offset of counters from beginning of struct
179 uint category_offset; // offset of category in the shared area
180 char name[]; // null terminated
181 // data follows: this is always 8-byte aligned
187 FTYPE_CATEGORY = 'C',
189 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
190 FTYPE_INSTANCE = 'I',
203 unsigned short num_counters;
204 unsigned short counters_data_size;
206 /* variable length data follows */
212 unsigned int category_offset;
213 /* variable length data follows */
219 /* variable length data follows */
228 unsigned int instance_type : 6;
236 unsigned short offset; // offset inside MonoPerfCounters
241 #undef PERFCTR_COUNTER
242 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
243 #define PERFCTR_COUNTER(id,name,help,type,field)
244 static const CategoryDesc
245 predef_categories [] = {
246 #include "mono-perfcounters-def.h"
247 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
251 #undef PERFCTR_COUNTER
252 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
253 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
254 static const CounterDesc
255 predef_counters [] = {
256 #include "mono-perfcounters-def.h"
257 {NULL, NULL, -1, 0, 0}
261 * We have several different classes of counters:
263 * *) runtime counters
265 * *) user-defined counters
266 * *) windows counters (the implementation on windows will use this)
268 * To easily handle the differences we create a vtable for each class that contains the
269 * function pointers with the actual implementation to access the counters.
271 typedef struct _ImplVtable ImplVtable;
273 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
274 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
275 typedef void (*CleanupFunc) (ImplVtable *vtable);
286 MonoPerfCounters *counters;
291 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
293 ImplVtable *vtable = g_new0 (ImplVtable, 1);
295 vtable->sample = sample;
296 vtable->update = update;
300 MonoPerfCounters *mono_perfcounters = NULL;
301 static MonoSharedArea *shared_area = NULL;
308 /* maps a pid to a ExternalSArea pointer */
309 static GHashTable *pid_to_shared_area = NULL;
311 static MonoSharedArea *
312 load_sarea_for_pid (int pid)
315 MonoSharedArea *area = NULL;
318 if (pid_to_shared_area == NULL)
319 pid_to_shared_area = g_hash_table_new (NULL, NULL);
320 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
322 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
324 data = g_new (ExternalSArea, 1);
327 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
338 predef_cleanup (ImplVtable *vtable)
340 PredefVtable *vt = (PredefVtable*)vtable;
343 if (!pid_to_shared_area) {
347 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
350 if (!data->refcount) {
351 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
352 mono_shared_area_unload (data->sarea);
360 mono_perfcounters_init (void)
362 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
366 InitializeCriticalSection (&perfctr_mutex);
368 shared_area = mono_shared_area ();
369 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
370 shared_area->counters_size = sizeof (MonoPerfCounters);
371 shared_area->data_start = d_offset;
372 shared_area->size = 4096;
373 mono_perfcounters = &shared_area->counters;
377 perfctr_type_compress (int type)
380 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
381 if (simple_type_to_type [i] == type)
384 /* NumberOfItems32 */
388 static unsigned char*
389 shared_data_find_room (int size)
391 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
392 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
397 unsigned short *next;
398 if (*p == FTYPE_END) {
399 if (size < (end - p))
405 next = (unsigned short*)(p + 2);
406 if (*p == FTYPE_DELETED) {
407 /* we reuse only if it's the same size */
417 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
420 foreach_shared_item (SharedFunc func, void *data)
422 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
423 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
426 unsigned short *next;
429 next = (unsigned short*)(p + 2);
430 if (!func ((SharedHeader*)p, data))
439 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
441 /* FIXME: make this case insensitive */
442 guint16 *strc = mono_string_chars (str);
443 while (*strc == *ascii_str++) {
448 return *strc - *(const unsigned char *)(ascii_str - 1);
457 category_search (SharedHeader *header, void *data)
459 CatSearch *search = data;
460 if (header->ftype == FTYPE_CATEGORY) {
461 SharedCategory *cat = (SharedCategory*)header;
462 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
470 static SharedCategory*
471 find_custom_category (MonoString *name)
476 foreach_shared_item (category_search, &search);
481 category_collect (SharedHeader *header, void *data)
483 GSList **list = data;
484 if (header->ftype == FTYPE_CATEGORY) {
485 *list = g_slist_prepend (*list, header);
491 get_custom_categories (void) {
493 foreach_shared_item (category_collect, &list);
498 custom_category_counters (SharedCategory* cat)
500 char *p = cat->name + strlen (cat->name) + 1;
501 p += strlen (p) + 1; /* skip category help */
505 static SharedCounter*
506 find_custom_counter (SharedCategory* cat, MonoString *name)
509 char *p = custom_category_counters (cat);
510 for (i = 0; i < cat->num_counters; ++i) {
511 SharedCounter *counter = (SharedCounter*)p;
512 if (mono_string_compare_ascii (name, counter->name) == 0)
514 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
515 p += strlen (p) + 1; /* skip counter help */
521 custom_category_help (SharedCategory* cat)
523 return cat->name + strlen (cat->name) + 1;
526 static const CounterDesc*
527 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
529 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
530 const CounterDesc *end = &predef_counters [desc [1].first_counter];
531 for (; cdesc < end; ++cdesc) {
532 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
538 /* fill the info in sample (except the raw value) */
540 fill_sample (MonoCounterSample *sample)
542 sample->timeStamp = mono_100ns_ticks ();
543 sample->timeStamp100nSec = sample->timeStamp;
544 sample->counterTimeStamp = sample->timeStamp;
545 sample->counterFrequency = 10000000;
546 sample->systemFrequency = 10000000;
547 // the real basevalue needs to be get from a different counter...
548 sample->baseValue = 0;
552 id_from_string (MonoString *instance, gboolean is_process)
555 if (mono_string_length (instance)) {
556 char *id_str = mono_string_to_utf8 (instance);
558 id = strtol (id_str, &end, 0);
559 if (end == id_str && !is_process)
567 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
569 MonoProcessError error;
570 int id = GPOINTER_TO_INT (vtable->arg);
574 fill_sample (sample);
575 sample->baseValue = 1;
577 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
579 case COUNTER_CPU_USER_TIME:
580 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
582 case COUNTER_CPU_PRIV_TIME:
583 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
585 case COUNTER_CPU_INTR_TIME:
586 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
588 case COUNTER_CPU_DCP_TIME:
589 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
591 case COUNTER_CPU_PROC_TIME:
592 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
599 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
601 int id = id_from_string (instance, FALSE) << 5;
602 const CounterDesc *cdesc;
604 /* increase the shift above and the mask also in the implementation functions */
605 //g_assert (32 > desc [1].first_counter - desc->first_counter);
606 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
608 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
614 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
616 int id = GPOINTER_TO_INT (vtable->arg);
622 fill_sample (sample);
623 sample->baseValue = 1;
625 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
627 case COUNTER_PROC_USER_TIME:
628 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
630 case COUNTER_PROC_PRIV_TIME:
631 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
633 case COUNTER_PROC_PROC_TIME:
634 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
636 case COUNTER_PROC_THREADS:
637 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
639 case COUNTER_PROC_VBYTES:
640 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
642 case COUNTER_PROC_WSET:
643 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
645 case COUNTER_PROC_PBYTES:
646 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
653 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
655 int id = id_from_string (instance, TRUE) << 5;
656 const CounterDesc *cdesc;
658 /* increase the shift above and the mask also in the implementation functions */
659 //g_assert (32 > desc [1].first_counter - desc->first_counter);
660 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
662 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
668 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
670 int id = GPOINTER_TO_INT (vtable->arg);
672 fill_sample (sample);
673 sample->baseValue = 1;
675 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
677 case COUNTER_MEM_NUM_OBJECTS:
678 sample->rawValue = mono_stats.new_object_count;
685 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
687 const CounterDesc *cdesc;
689 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
691 return create_vtable (GINT_TO_POINTER (cdesc->id), mono_mem_counter, NULL);
697 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
699 PredefVtable *vt = (PredefVtable *)vtable;
700 const CounterDesc *desc;
701 int cat_id = GPOINTER_TO_INT (vtable->arg);
702 int id = cat_id >> 16;
705 fill_sample (sample);
706 sample->baseValue = 1;
708 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
709 sample->counterType = desc->type;
710 /* FIXME: check that the offset fits inside imported counters */
711 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
712 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
717 predef_vtable (void *arg, MonoString *instance)
719 MonoSharedArea *area;
720 PredefVtable *vtable;
721 char *pids = mono_string_to_utf8 (instance);
726 area = load_sarea_for_pid (pid);
730 vtable = g_new (PredefVtable, 1);
731 vtable->vtable.arg = arg;
732 vtable->vtable.sample = predef_readonly_counter;
733 vtable->vtable.cleanup = predef_cleanup;
734 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
737 return (ImplVtable*)vtable;
740 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
741 * this needs some way to set sample->counterType as well, though.
744 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
746 int cat_id = GPOINTER_TO_INT (vtable->arg);
747 int id = cat_id >> 16;
750 fill_sample (sample);
751 sample->baseValue = 1;
753 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
755 case CATEGORY_ASPNET:
757 case COUNTER_ASPNET_REQ_Q:
758 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
767 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
770 int cat_id = GPOINTER_TO_INT (vtable->arg);
771 int id = cat_id >> 16;
774 case CATEGORY_ASPNET:
776 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
782 /* FIXME: we need to do this atomically */
786 /* this can be non-atomic */
794 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
796 const CounterDesc *cdesc;
798 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
800 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
801 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
803 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
809 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
811 SharedCounter *scounter = vtable->arg;
813 fill_sample (sample);
814 sample->baseValue = 1;
816 sample->counterType = simple_type_to_type [scounter->type];
818 sample->rawValue = 0;
823 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
829 /* FIXME: we need to do this atomically */
833 /* this can be non-atomic */
841 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
843 SharedCounter *scounter;
845 scounter = find_custom_counter (cat, counter);
848 *type = simple_type_to_type [scounter->type];
849 /* FIXME: use instance */
850 return create_vtable (scounter, custom_writable_counter, custom_writable_update);
853 static const CategoryDesc*
854 find_category (MonoString *category)
857 for (i = 0; i < NUM_CATEGORIES; ++i) {
858 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
859 return &predef_categories [i];
865 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
866 MonoString* machine, int *type, MonoBoolean *custom)
868 const CategoryDesc *cdesc;
869 /* no support for counters on other machines */
870 if (mono_string_compare_ascii (machine, "."))
872 cdesc = find_category (category);
874 SharedCategory *scat = find_custom_category (category);
878 return custom_get_impl (scat, counter, instance, type);
882 return cpu_get_impl (counter, instance, type, custom);
884 return process_get_impl (counter, instance, type, custom);
885 case CATEGORY_MONO_MEM:
886 return mono_mem_get_impl (counter, instance, type, custom);
890 case CATEGORY_REMOTING:
891 case CATEGORY_LOADING:
892 case CATEGORY_THREAD:
893 case CATEGORY_INTEROP:
894 case CATEGORY_SECURITY:
895 case CATEGORY_ASPNET:
896 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
902 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
904 ImplVtable *vtable = impl;
905 if (vtable && vtable->sample)
906 return vtable->sample (vtable, only_value, sample);
911 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
913 ImplVtable *vtable = impl;
914 if (vtable && vtable->update)
915 return vtable->update (vtable, do_incr, value);
920 mono_perfcounter_free_data (void *impl)
922 ImplVtable *vtable = impl;
923 if (vtable && vtable->cleanup)
924 vtable->cleanup (vtable);
928 /* Category icalls */
930 mono_perfcounter_category_del (MonoString *name)
932 const CategoryDesc *cdesc;
934 cdesc = find_category (name);
935 /* can't delete a predefined category */
939 cat = find_custom_category (name);
940 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
941 if (!cat || cat->num_instances) {
945 cat->header.ftype = FTYPE_DELETED;
951 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
953 const CategoryDesc *cdesc;
954 /* no support for counters on other machines */
955 if (mono_string_compare_ascii (machine, "."))
957 cdesc = find_category (category);
959 SharedCategory *scat = find_custom_category (category);
962 return mono_string_new (mono_domain_get (), custom_category_help (scat));
964 return mono_string_new (mono_domain_get (), cdesc->help);
968 * Check if the category named @category exists on @machine. If @counter is not NULL, return
969 * TRUE only if a counter with that name exists in the category.
972 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
974 const CategoryDesc *cdesc;
975 /* no support for counters on other machines */
976 if (mono_string_compare_ascii (machine, "."))
978 cdesc = find_category (category);
980 SharedCategory *scat = find_custom_category (category);
983 /* counter is allowed to be null */
986 /* search through the custom category */
987 return find_custom_counter (scat, counter) != NULL;
989 /* counter is allowed to be null */
992 if (get_counter_in_category (cdesc, counter))
997 /* C map of the type with the same name */
1003 } CounterCreationData;
1006 * Since we'll keep a copy of the category per-process, we should also make sure
1007 * categories with the same name are compatible.
1010 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1014 int num_counters = mono_array_length (items);
1015 int counters_data_size;
1016 char *name = mono_string_to_utf8 (category);
1017 char *chelp = mono_string_to_utf8 (help);
1018 char **counter_info;
1021 SharedCategory *cat;
1023 counter_info = g_new0 (char*, num_counters * 2);
1024 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1025 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1026 for (i = 0; i < num_counters; ++i) {
1027 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1028 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1029 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1030 size += 3; /* type and two 0 string terminators */
1032 for (i = 0; i < num_counters * 2; ++i) {
1033 if (!counter_info [i])
1035 size += strlen (counter_info [i]);
1037 counters_data_size = num_counters * 8; /* optimize for size later */
1041 ptr = shared_data_find_room (size);
1046 cat = (SharedCategory*)ptr;
1047 cat->header.extra = type;
1048 cat->header.size = size;
1049 cat->num_counters = num_counters;
1050 cat->counters_data_size = counters_data_size;
1051 /* now copy the vaiable data */
1054 p += strlen (name) + 1;
1056 p += strlen (chelp) + 1;
1057 for (i = 0; i < num_counters; ++i) {
1058 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1059 *p++ = perfctr_type_compress (data->type);
1060 strcpy (p, counter_info [i * 2]);
1061 p += strlen (counter_info [i * 2]) + 1;
1062 strcpy (p, counter_info [i * 2 + 1]);
1063 p += strlen (counter_info [i * 2 + 1]) + 1;
1065 cat->header.ftype = FTYPE_CATEGORY;
1070 for (i = 0; i < num_counters * 2; ++i) {
1071 g_free (counter_info [i]);
1073 g_free (counter_info);
1080 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1082 const CategoryDesc *cdesc;
1083 /* no support for counters on other machines */
1084 if (mono_string_compare_ascii (machine, "."))
1086 cdesc = find_category (category);
1093 mono_perfcounter_category_names (MonoString *machine)
1097 MonoDomain *domain = mono_domain_get ();
1098 GSList *custom_categories, *tmp;
1099 /* no support for counters on other machines */
1100 if (mono_string_compare_ascii (machine, "."))
1101 return mono_array_new (domain, mono_get_string_class (), 0);
1103 custom_categories = get_custom_categories ();
1104 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1105 for (i = 0; i < NUM_CATEGORIES; ++i) {
1106 const CategoryDesc *cdesc = &predef_categories [i];
1107 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1109 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1110 SharedCategory *scat = tmp->data;
1111 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1115 g_slist_free (custom_categories);
1120 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1123 SharedCategory *scat;
1124 const CategoryDesc *cdesc;
1126 MonoDomain *domain = mono_domain_get ();
1127 /* no support for counters on other machines */
1128 if (mono_string_compare_ascii (machine, "."))
1129 return mono_array_new (domain, mono_get_string_class (), 0);
1130 cdesc = find_category (category);
1132 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1133 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1134 const CounterDesc *desc = &predef_counters [i];
1135 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1140 scat = find_custom_category (category);
1142 char *p = custom_category_counters (scat);
1144 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1145 for (i = 0; i < scat->num_counters; ++i) {
1146 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1147 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1148 p += strlen (p) + 1; /* skip counter help */
1154 return mono_array_new (domain, mono_get_string_class (), 0);
1158 get_string_array (void **array, int count, gboolean is_process)
1161 MonoDomain *domain = mono_domain_get ();
1162 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1163 for (i = 0; i < count; ++i) {
1167 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1168 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1170 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1173 mono_array_setref (res, i, mono_string_new (domain, p));
1181 get_mono_instances (void)
1190 buf = g_new (void*, count);
1191 res = mono_shared_area_instances (buf, count);
1192 } while (res == count);
1193 array = get_string_array (buf, res, TRUE);
1199 get_cpu_instances (void)
1204 count = mono_cpu_count ();
1205 buf = g_new (void*, count);
1206 for (i = 0; i < count; ++i)
1207 buf [i] = GINT_TO_POINTER (i);
1208 array = get_string_array (buf, count, FALSE);
1214 get_processes_instances (void)
1218 void **buf = mono_process_list (&count);
1220 return get_string_array (NULL, 0, FALSE);
1221 array = get_string_array (buf, count, TRUE);
1227 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1229 const CategoryDesc* cat;
1230 if (mono_string_compare_ascii (machine, "."))
1231 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1232 cat = find_category (category);
1234 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1235 switch (cat->instance_type) {
1237 return get_mono_instances ();
1239 return get_cpu_instances ();
1240 case ProcessInstance:
1241 return get_processes_instances ();
1242 case CustomInstance:
1243 case ThreadInstance:
1245 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);