4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.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 */
214 char instance_name [1];
220 /* variable length data follows */
229 unsigned int instance_type : 6;
237 unsigned short offset; // offset inside MonoPerfCounters
242 #undef PERFCTR_COUNTER
243 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
244 #define PERFCTR_COUNTER(id,name,help,type,field)
245 static const CategoryDesc
246 predef_categories [] = {
247 #include "mono-perfcounters-def.h"
248 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
252 #undef PERFCTR_COUNTER
253 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
254 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
255 static const CounterDesc
256 predef_counters [] = {
257 #include "mono-perfcounters-def.h"
258 {NULL, NULL, -1, 0, 0}
262 * We have several different classes of counters:
264 * *) runtime counters
266 * *) user-defined counters
267 * *) windows counters (the implementation on windows will use this)
269 * To easily handle the differences we create a vtable for each class that contains the
270 * function pointers with the actual implementation to access the counters.
272 typedef struct _ImplVtable ImplVtable;
274 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
275 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
276 typedef void (*CleanupFunc) (ImplVtable *vtable);
287 MonoPerfCounters *counters;
293 SharedInstance *instance_desc;
294 SharedCounter *counter_desc;
298 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
300 ImplVtable *vtable = g_new0 (ImplVtable, 1);
302 vtable->sample = sample;
303 vtable->update = update;
307 MonoPerfCounters *mono_perfcounters = NULL;
308 static MonoSharedArea *shared_area = NULL;
315 /* maps a pid to a ExternalSArea pointer */
316 static GHashTable *pid_to_shared_area = NULL;
318 static MonoSharedArea *
319 load_sarea_for_pid (int pid)
322 MonoSharedArea *area = NULL;
325 if (pid_to_shared_area == NULL)
326 pid_to_shared_area = g_hash_table_new (NULL, NULL);
327 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
329 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
331 data = g_new (ExternalSArea, 1);
334 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
345 unref_pid_unlocked (int pid)
348 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
351 if (!data->refcount) {
352 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
353 mono_shared_area_unload (data->sarea);
360 predef_cleanup (ImplVtable *vtable)
362 PredefVtable *vt = (PredefVtable*)vtable;
363 /* ExternalSArea *data; */
366 if (!pid_to_shared_area) {
370 unref_pid_unlocked (vt->pid);
375 mono_perfcounters_init (void)
377 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
381 InitializeCriticalSection (&perfctr_mutex);
383 shared_area = mono_shared_area ();
384 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
385 shared_area->counters_size = sizeof (MonoPerfCounters);
386 shared_area->data_start = d_offset;
387 shared_area->size = 4096;
388 mono_perfcounters = &shared_area->counters;
392 perfctr_type_compress (int type)
395 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
396 if (simple_type_to_type [i] == type)
399 /* NumberOfItems32 */
403 static unsigned char*
404 shared_data_find_room (int size)
406 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
407 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
412 unsigned short *next;
413 if (*p == FTYPE_END) {
414 if (size < (end - p))
420 next = (unsigned short*)(p + 2);
421 if (*p == FTYPE_DELETED) {
422 /* we reuse only if it's the same size */
432 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
435 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
438 unsigned short *next;
441 next = (unsigned short*)(p + 2);
442 if (!func ((SharedHeader*)p, data))
451 foreach_shared_item (SharedFunc func, void *data)
453 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
454 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
456 foreach_shared_item_in_area (p, end, func, data);
460 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
462 /* FIXME: make this case insensitive */
463 guint16 *strc = mono_string_chars (str);
464 while (*strc == *ascii_str++) {
469 return *strc - *(const unsigned char *)(ascii_str - 1);
478 category_search (SharedHeader *header, void *data)
480 CatSearch *search = data;
481 if (header->ftype == FTYPE_CATEGORY) {
482 SharedCategory *cat = (SharedCategory*)header;
483 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
491 static SharedCategory*
492 find_custom_category (MonoString *name)
497 foreach_shared_item (category_search, &search);
502 category_collect (SharedHeader *header, void *data)
504 GSList **list = data;
505 if (header->ftype == FTYPE_CATEGORY) {
506 *list = g_slist_prepend (*list, header);
512 get_custom_categories (void) {
514 foreach_shared_item (category_collect, &list);
519 custom_category_counters (SharedCategory* cat)
521 char *p = cat->name + strlen (cat->name) + 1;
522 p += strlen (p) + 1; /* skip category help */
526 static SharedCounter*
527 find_custom_counter (SharedCategory* cat, MonoString *name)
530 char *p = custom_category_counters (cat);
531 for (i = 0; i < cat->num_counters; ++i) {
532 SharedCounter *counter = (SharedCounter*)p;
533 if (mono_string_compare_ascii (name, counter->name) == 0)
535 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
536 p += strlen (p) + 1; /* skip counter help */
542 unsigned int cat_offset;
544 MonoString *instance;
545 SharedInstance* result;
550 instance_search (SharedHeader *header, void *data)
552 InstanceSearch *search = data;
553 if (header->ftype == FTYPE_INSTANCE) {
554 SharedInstance *ins = (SharedInstance*)header;
555 if (search->cat_offset == ins->category_offset) {
556 if (search->instance) {
557 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
558 search->result = ins;
562 search->list = g_slist_prepend (search->list, ins);
569 static SharedInstance*
570 find_custom_instance (SharedCategory* cat, MonoString *instance)
572 InstanceSearch search;
573 search.cat_offset = (char*)cat - (char*)shared_area;
575 search.instance = instance;
577 search.result = NULL;
578 foreach_shared_item (instance_search, &search);
579 return search.result;
583 get_custom_instances_list (SharedCategory* cat)
585 InstanceSearch search;
586 search.cat_offset = (char*)cat - (char*)shared_area;
588 search.instance = NULL;
590 search.result = NULL;
591 foreach_shared_item (instance_search, &search);
596 custom_category_help (SharedCategory* cat)
598 return cat->name + strlen (cat->name) + 1;
601 static const CounterDesc*
602 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
604 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
605 const CounterDesc *end = &predef_counters [desc [1].first_counter];
606 for (; cdesc < end; ++cdesc) {
607 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
613 /* fill the info in sample (except the raw value) */
615 fill_sample (MonoCounterSample *sample)
617 sample->timeStamp = mono_100ns_ticks ();
618 sample->timeStamp100nSec = sample->timeStamp;
619 sample->counterTimeStamp = sample->timeStamp;
620 sample->counterFrequency = 10000000;
621 sample->systemFrequency = 10000000;
622 // the real basevalue needs to be get from a different counter...
623 sample->baseValue = 0;
627 id_from_string (MonoString *instance, gboolean is_process)
630 if (mono_string_length (instance)) {
631 char *id_str = mono_string_to_utf8 (instance);
633 id = strtol (id_str, &end, 0);
634 if (end == id_str && !is_process)
642 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
644 MonoProcessError error;
645 int id = GPOINTER_TO_INT (vtable->arg);
649 fill_sample (sample);
650 sample->baseValue = 1;
652 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
654 case COUNTER_CPU_USER_TIME:
655 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
657 case COUNTER_CPU_PRIV_TIME:
658 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
660 case COUNTER_CPU_INTR_TIME:
661 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
663 case COUNTER_CPU_DCP_TIME:
664 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
666 case COUNTER_CPU_PROC_TIME:
667 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
674 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
676 int id = id_from_string (instance, FALSE) << 5;
677 const CounterDesc *cdesc;
679 /* increase the shift above and the mask also in the implementation functions */
680 //g_assert (32 > desc [1].first_counter - desc->first_counter);
681 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
683 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
689 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
691 int id = GPOINTER_TO_INT (vtable->arg);
697 fill_sample (sample);
698 sample->baseValue = 1;
700 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
702 case COUNTER_PROC_USER_TIME:
703 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
705 case COUNTER_PROC_PRIV_TIME:
706 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
708 case COUNTER_PROC_PROC_TIME:
709 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
711 case COUNTER_PROC_THREADS:
712 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
714 case COUNTER_PROC_VBYTES:
715 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
717 case COUNTER_PROC_WSET:
718 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
720 case COUNTER_PROC_PBYTES:
721 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
728 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
730 int id = id_from_string (instance, TRUE) << 5;
731 const CounterDesc *cdesc;
733 /* increase the shift above and the mask also in the implementation functions */
734 //g_assert (32 > desc [1].first_counter - desc->first_counter);
735 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
737 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
743 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
745 int id = GPOINTER_TO_INT (vtable->arg);
747 fill_sample (sample);
748 sample->baseValue = 1;
750 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
752 case COUNTER_MEM_NUM_OBJECTS:
753 sample->rawValue = mono_stats.new_object_count;
760 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
762 const CounterDesc *cdesc;
764 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
766 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
772 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
774 PredefVtable *vt = (PredefVtable *)vtable;
775 const CounterDesc *desc;
776 int cat_id = GPOINTER_TO_INT (vtable->arg);
777 int id = cat_id >> 16;
780 fill_sample (sample);
781 sample->baseValue = 1;
783 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
784 sample->counterType = desc->type;
785 /* FIXME: check that the offset fits inside imported counters */
786 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
787 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
792 predef_vtable (void *arg, MonoString *instance)
794 MonoSharedArea *area;
795 PredefVtable *vtable;
796 char *pids = mono_string_to_utf8 (instance);
801 area = load_sarea_for_pid (pid);
805 vtable = g_new (PredefVtable, 1);
806 vtable->vtable.arg = arg;
807 vtable->vtable.sample = predef_readonly_counter;
808 vtable->vtable.cleanup = predef_cleanup;
809 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
812 return (ImplVtable*)vtable;
815 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
816 * this needs some way to set sample->counterType as well, though.
819 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
821 int cat_id = GPOINTER_TO_INT (vtable->arg);
822 int id = cat_id >> 16;
825 fill_sample (sample);
826 sample->baseValue = 1;
828 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
832 case COUNTER_EXC_THROWN:
833 sample->rawValue = mono_perfcounters->exceptions_thrown;
837 case CATEGORY_ASPNET:
839 case COUNTER_ASPNET_REQ_Q:
840 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
842 case COUNTER_ASPNET_REQ_TOTAL:
843 sample->rawValue = mono_perfcounters->aspnet_requests;
852 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
855 int cat_id = GPOINTER_TO_INT (vtable->arg);
856 int id = cat_id >> 16;
859 case CATEGORY_ASPNET:
861 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
862 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
868 /* FIXME: we need to do this atomically */
872 /* this can be non-atomic */
880 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
882 const CounterDesc *cdesc;
884 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
886 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
887 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
889 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
895 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
897 CustomVTable *counter_data = (CustomVTable *)vtable;
899 fill_sample (sample);
900 sample->baseValue = 1;
902 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
904 sample->rawValue = 0;
906 sample->rawValue = *(guint64*)vtable->arg;
911 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
913 /* FIXME: check writability */
914 guint64 *ptr = vtable->arg;
917 /* FIXME: we need to do this atomically */
921 /* this can be non-atomic */
928 static SharedInstance*
929 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
931 SharedInstance* inst;
934 int size, data_offset;
936 inst = find_custom_instance (cat, instance);
939 name = mono_string_to_utf8 (instance);
940 size = sizeof (SharedInstance) + strlen (name);
944 size += (sizeof (guint64) * cat->num_counters);
946 ptr = shared_data_find_room (size);
952 inst = (SharedInstance*)ptr;
953 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
954 inst->header.size = size;
955 inst->category_offset = (char*)cat - (char*)shared_area;
956 cat->num_instances++;
957 /* now copy the variable data */
958 p = inst->instance_name;
960 p += strlen (name) + 1;
961 inst->header.ftype = FTYPE_INSTANCE;
969 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
971 CustomVTable* vtable;
972 vtable = g_new0 (CustomVTable, 1);
973 vtable->vtable.arg = data;
974 vtable->vtable.sample = custom_writable_counter;
975 vtable->vtable.update = custom_writable_update;
976 vtable->instance_desc = inst;
977 vtable->counter_desc = scounter;
979 return (ImplVtable*)vtable;
983 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
985 SharedCounter *scounter;
986 SharedInstance* inst;
989 scounter = find_custom_counter (cat, counter);
992 *type = simple_type_to_type [scounter->type];
993 inst = custom_get_instance (cat, scounter, instance);
996 size = sizeof (SharedInstance) + strlen (inst->instance_name);
999 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1002 static const CategoryDesc*
1003 find_category (MonoString *category)
1006 for (i = 0; i < NUM_CATEGORIES; ++i) {
1007 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1008 return &predef_categories [i];
1014 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1015 MonoString* machine, int *type, MonoBoolean *custom)
1017 const CategoryDesc *cdesc;
1018 /* no support for counters on other machines */
1019 if (mono_string_compare_ascii (machine, "."))
1021 cdesc = find_category (category);
1023 SharedCategory *scat = find_custom_category (category);
1027 return custom_get_impl (scat, counter, instance, type);
1029 switch (cdesc->id) {
1031 return cpu_get_impl (counter, instance, type, custom);
1033 return process_get_impl (counter, instance, type, custom);
1034 case CATEGORY_MONO_MEM:
1035 return mono_mem_get_impl (counter, instance, type, custom);
1039 case CATEGORY_REMOTING:
1040 case CATEGORY_LOADING:
1041 case CATEGORY_THREAD:
1042 case CATEGORY_INTEROP:
1043 case CATEGORY_SECURITY:
1044 case CATEGORY_ASPNET:
1045 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1051 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1053 ImplVtable *vtable = impl;
1054 if (vtable && vtable->sample)
1055 return vtable->sample (vtable, only_value, sample);
1060 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1062 ImplVtable *vtable = impl;
1063 if (vtable && vtable->update)
1064 return vtable->update (vtable, do_incr, value);
1069 mono_perfcounter_free_data (void *impl)
1071 ImplVtable *vtable = impl;
1072 if (vtable && vtable->cleanup)
1073 vtable->cleanup (vtable);
1077 /* Category icalls */
1079 mono_perfcounter_category_del (MonoString *name)
1081 const CategoryDesc *cdesc;
1082 SharedCategory *cat;
1083 cdesc = find_category (name);
1084 /* can't delete a predefined category */
1088 cat = find_custom_category (name);
1089 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1090 if (!cat || cat->num_instances) {
1094 cat->header.ftype = FTYPE_DELETED;
1100 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1102 const CategoryDesc *cdesc;
1103 /* no support for counters on other machines */
1104 if (mono_string_compare_ascii (machine, "."))
1106 cdesc = find_category (category);
1108 SharedCategory *scat = find_custom_category (category);
1111 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1113 return mono_string_new (mono_domain_get (), cdesc->help);
1117 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1118 * TRUE only if a counter with that name exists in the category.
1121 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1123 const CategoryDesc *cdesc;
1124 /* no support for counters on other machines */
1125 if (mono_string_compare_ascii (machine, "."))
1127 cdesc = find_category (category);
1129 SharedCategory *scat = find_custom_category (category);
1132 /* counter is allowed to be null */
1135 /* search through the custom category */
1136 return find_custom_counter (scat, counter) != NULL;
1138 /* counter is allowed to be null */
1141 if (get_counter_in_category (cdesc, counter))
1146 /* C map of the type with the same name */
1152 } CounterCreationData;
1155 * Since we'll keep a copy of the category per-process, we should also make sure
1156 * categories with the same name are compatible.
1159 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1163 int num_counters = mono_array_length (items);
1164 int counters_data_size;
1167 char **counter_info;
1170 SharedCategory *cat;
1172 /* FIXME: ensure there isn't a category created already */
1173 name = mono_string_to_utf8 (category);
1174 chelp = mono_string_to_utf8 (help);
1175 counter_info = g_new0 (char*, num_counters * 2);
1176 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1177 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1178 for (i = 0; i < num_counters; ++i) {
1179 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1180 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1181 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1182 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1184 for (i = 0; i < num_counters * 2; ++i) {
1185 if (!counter_info [i])
1187 size += strlen (counter_info [i]) + 1;
1191 counters_data_size = num_counters * 8; /* optimize for size later */
1195 ptr = shared_data_find_room (size);
1200 cat = (SharedCategory*)ptr;
1201 cat->header.extra = type;
1202 cat->header.size = size;
1203 cat->num_counters = num_counters;
1204 cat->counters_data_size = counters_data_size;
1205 /* now copy the vaiable data */
1208 p += strlen (name) + 1;
1210 p += strlen (chelp) + 1;
1211 for (i = 0; i < num_counters; ++i) {
1212 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1213 /* emit the SharedCounter structures */
1214 *p++ = perfctr_type_compress (data->type);
1216 strcpy (p, counter_info [i * 2]);
1217 p += strlen (counter_info [i * 2]) + 1;
1218 strcpy (p, counter_info [i * 2 + 1]);
1219 p += strlen (counter_info [i * 2 + 1]) + 1;
1221 cat->header.ftype = FTYPE_CATEGORY;
1226 for (i = 0; i < num_counters * 2; ++i) {
1227 g_free (counter_info [i]);
1229 g_free (counter_info);
1236 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1238 const CategoryDesc *cdesc;
1239 /* no support for counters on other machines */
1240 /*FIXME: machine appears to be wrong
1241 if (mono_string_compare_ascii (machine, "."))
1243 cdesc = find_category (category);
1245 SharedCategory *scat;
1246 scat = find_custom_category (category);
1249 if (find_custom_instance (scat, instance))
1252 /* FIXME: search instance */
1258 mono_perfcounter_category_names (MonoString *machine)
1262 MonoDomain *domain = mono_domain_get ();
1263 GSList *custom_categories, *tmp;
1264 /* no support for counters on other machines */
1265 if (mono_string_compare_ascii (machine, "."))
1266 return mono_array_new (domain, mono_get_string_class (), 0);
1268 custom_categories = get_custom_categories ();
1269 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1270 for (i = 0; i < NUM_CATEGORIES; ++i) {
1271 const CategoryDesc *cdesc = &predef_categories [i];
1272 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1274 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1275 SharedCategory *scat = tmp->data;
1276 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1280 g_slist_free (custom_categories);
1285 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1288 SharedCategory *scat;
1289 const CategoryDesc *cdesc;
1291 MonoDomain *domain = mono_domain_get ();
1292 /* no support for counters on other machines */
1293 if (mono_string_compare_ascii (machine, "."))
1294 return mono_array_new (domain, mono_get_string_class (), 0);
1295 cdesc = find_category (category);
1297 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1298 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1299 const CounterDesc *desc = &predef_counters [i];
1300 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1305 scat = find_custom_category (category);
1307 char *p = custom_category_counters (scat);
1309 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1310 for (i = 0; i < scat->num_counters; ++i) {
1311 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1312 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1313 p += strlen (p) + 1; /* skip counter help */
1319 return mono_array_new (domain, mono_get_string_class (), 0);
1323 get_string_array (void **array, int count, gboolean is_process)
1326 MonoDomain *domain = mono_domain_get ();
1327 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1328 for (i = 0; i < count; ++i) {
1332 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1333 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1335 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1338 mono_array_setref (res, i, mono_string_new (domain, p));
1346 get_mono_instances (void)
1355 buf = g_new (void*, count);
1356 res = mono_shared_area_instances (buf, count);
1357 } while (res == count);
1358 array = get_string_array (buf, res, TRUE);
1364 get_cpu_instances (void)
1369 count = mono_cpu_count ();
1370 buf = g_new (void*, count);
1371 for (i = 0; i < count; ++i)
1372 buf [i] = GINT_TO_POINTER (i);
1373 array = get_string_array (buf, count, FALSE);
1379 get_processes_instances (void)
1383 void **buf = mono_process_list (&count);
1385 return get_string_array (NULL, 0, FALSE);
1386 array = get_string_array (buf, count, TRUE);
1392 get_custom_instances (MonoString *category)
1394 SharedCategory *scat;
1395 scat = find_custom_category (category);
1397 GSList *list = get_custom_instances_list (scat);
1400 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1401 for (tmp = list; tmp; tmp = tmp->next) {
1402 SharedInstance *inst = tmp->data;
1403 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1406 g_slist_free (list);
1409 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1413 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1415 const CategoryDesc* cat;
1416 if (mono_string_compare_ascii (machine, "."))
1417 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1418 cat = find_category (category);
1420 return get_custom_instances (category);
1421 switch (cat->instance_type) {
1423 return get_mono_instances ();
1425 return get_cpu_instances ();
1426 case ProcessInstance:
1427 return get_processes_instances ();
1428 case ThreadInstance:
1430 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);