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;
365 if (!pid_to_shared_area) {
369 unref_pid_unlocked (vt->pid);
374 mono_perfcounters_init (void)
376 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
380 InitializeCriticalSection (&perfctr_mutex);
382 shared_area = mono_shared_area ();
383 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
384 shared_area->counters_size = sizeof (MonoPerfCounters);
385 shared_area->data_start = d_offset;
386 shared_area->size = 4096;
387 mono_perfcounters = &shared_area->counters;
391 perfctr_type_compress (int type)
394 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
395 if (simple_type_to_type [i] == type)
398 /* NumberOfItems32 */
402 static unsigned char*
403 shared_data_find_room (int size)
405 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
406 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
411 unsigned short *next;
412 if (*p == FTYPE_END) {
413 if (size < (end - p))
419 next = (unsigned short*)(p + 2);
420 if (*p == FTYPE_DELETED) {
421 /* we reuse only if it's the same size */
431 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
434 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
437 unsigned short *next;
440 next = (unsigned short*)(p + 2);
441 if (!func ((SharedHeader*)p, data))
450 foreach_shared_item (SharedFunc func, void *data)
452 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
453 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
455 foreach_shared_item_in_area (p, end, func, data);
459 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
461 /* FIXME: make this case insensitive */
462 guint16 *strc = mono_string_chars (str);
463 while (*strc == *ascii_str++) {
468 return *strc - *(const unsigned char *)(ascii_str - 1);
477 category_search (SharedHeader *header, void *data)
479 CatSearch *search = data;
480 if (header->ftype == FTYPE_CATEGORY) {
481 SharedCategory *cat = (SharedCategory*)header;
482 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
490 static SharedCategory*
491 find_custom_category (MonoString *name)
496 foreach_shared_item (category_search, &search);
501 category_collect (SharedHeader *header, void *data)
503 GSList **list = data;
504 if (header->ftype == FTYPE_CATEGORY) {
505 *list = g_slist_prepend (*list, header);
511 get_custom_categories (void) {
513 foreach_shared_item (category_collect, &list);
518 custom_category_counters (SharedCategory* cat)
520 char *p = cat->name + strlen (cat->name) + 1;
521 p += strlen (p) + 1; /* skip category help */
525 static SharedCounter*
526 find_custom_counter (SharedCategory* cat, MonoString *name)
529 char *p = custom_category_counters (cat);
530 for (i = 0; i < cat->num_counters; ++i) {
531 SharedCounter *counter = (SharedCounter*)p;
532 if (mono_string_compare_ascii (name, counter->name) == 0)
534 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
535 p += strlen (p) + 1; /* skip counter help */
541 unsigned int cat_offset;
543 MonoString *instance;
544 SharedInstance* result;
549 instance_search (SharedHeader *header, void *data)
551 InstanceSearch *search = data;
552 if (header->ftype == FTYPE_INSTANCE) {
553 SharedInstance *ins = (SharedInstance*)header;
554 if (search->cat_offset == ins->category_offset) {
555 if (search->instance) {
556 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
557 search->result = ins;
561 search->list = g_slist_prepend (search->list, ins);
568 static SharedInstance*
569 find_custom_instance (SharedCategory* cat, MonoString *instance)
571 InstanceSearch search;
572 search.cat_offset = (char*)cat - (char*)shared_area;
574 search.instance = instance;
576 search.result = NULL;
577 foreach_shared_item (instance_search, &search);
578 return search.result;
582 get_custom_instances_list (SharedCategory* cat)
584 InstanceSearch search;
585 search.cat_offset = (char*)cat - (char*)shared_area;
587 search.instance = NULL;
589 search.result = NULL;
590 foreach_shared_item (instance_search, &search);
595 custom_category_help (SharedCategory* cat)
597 return cat->name + strlen (cat->name) + 1;
600 static const CounterDesc*
601 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
603 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
604 const CounterDesc *end = &predef_counters [desc [1].first_counter];
605 for (; cdesc < end; ++cdesc) {
606 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
612 /* fill the info in sample (except the raw value) */
614 fill_sample (MonoCounterSample *sample)
616 sample->timeStamp = mono_100ns_ticks ();
617 sample->timeStamp100nSec = sample->timeStamp;
618 sample->counterTimeStamp = sample->timeStamp;
619 sample->counterFrequency = 10000000;
620 sample->systemFrequency = 10000000;
621 // the real basevalue needs to be get from a different counter...
622 sample->baseValue = 0;
626 id_from_string (MonoString *instance, gboolean is_process)
629 if (mono_string_length (instance)) {
630 char *id_str = mono_string_to_utf8 (instance);
632 id = strtol (id_str, &end, 0);
633 if (end == id_str && !is_process)
641 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
643 MonoProcessError error;
644 int id = GPOINTER_TO_INT (vtable->arg);
648 fill_sample (sample);
649 sample->baseValue = 1;
651 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
653 case COUNTER_CPU_USER_TIME:
654 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
656 case COUNTER_CPU_PRIV_TIME:
657 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
659 case COUNTER_CPU_INTR_TIME:
660 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
662 case COUNTER_CPU_DCP_TIME:
663 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
665 case COUNTER_CPU_PROC_TIME:
666 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
673 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
675 int id = id_from_string (instance, FALSE) << 5;
676 const CounterDesc *cdesc;
678 /* increase the shift above and the mask also in the implementation functions */
679 //g_assert (32 > desc [1].first_counter - desc->first_counter);
680 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
682 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
688 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
690 int id = GPOINTER_TO_INT (vtable->arg);
696 fill_sample (sample);
697 sample->baseValue = 1;
699 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
701 case COUNTER_PROC_USER_TIME:
702 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
704 case COUNTER_PROC_PRIV_TIME:
705 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
707 case COUNTER_PROC_PROC_TIME:
708 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
710 case COUNTER_PROC_THREADS:
711 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
713 case COUNTER_PROC_VBYTES:
714 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
716 case COUNTER_PROC_WSET:
717 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
719 case COUNTER_PROC_PBYTES:
720 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
727 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
729 int id = id_from_string (instance, TRUE) << 5;
730 const CounterDesc *cdesc;
732 /* increase the shift above and the mask also in the implementation functions */
733 //g_assert (32 > desc [1].first_counter - desc->first_counter);
734 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
736 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
742 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
744 int id = GPOINTER_TO_INT (vtable->arg);
746 fill_sample (sample);
747 sample->baseValue = 1;
749 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
751 case COUNTER_MEM_NUM_OBJECTS:
752 sample->rawValue = mono_stats.new_object_count;
759 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
761 const CounterDesc *cdesc;
763 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
765 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
771 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
773 PredefVtable *vt = (PredefVtable *)vtable;
774 const CounterDesc *desc;
775 int cat_id = GPOINTER_TO_INT (vtable->arg);
776 int id = cat_id >> 16;
779 fill_sample (sample);
780 sample->baseValue = 1;
782 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
783 sample->counterType = desc->type;
784 /* FIXME: check that the offset fits inside imported counters */
785 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
786 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
791 predef_vtable (void *arg, MonoString *instance)
793 MonoSharedArea *area;
794 PredefVtable *vtable;
795 char *pids = mono_string_to_utf8 (instance);
800 area = load_sarea_for_pid (pid);
804 vtable = g_new (PredefVtable, 1);
805 vtable->vtable.arg = arg;
806 vtable->vtable.sample = predef_readonly_counter;
807 vtable->vtable.cleanup = predef_cleanup;
808 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
811 return (ImplVtable*)vtable;
814 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
815 * this needs some way to set sample->counterType as well, though.
818 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
820 int cat_id = GPOINTER_TO_INT (vtable->arg);
821 int id = cat_id >> 16;
824 fill_sample (sample);
825 sample->baseValue = 1;
827 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
831 case COUNTER_EXC_THROWN:
832 sample->rawValue = mono_perfcounters->exceptions_thrown;
836 case CATEGORY_ASPNET:
838 case COUNTER_ASPNET_REQ_Q:
839 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
841 case COUNTER_ASPNET_REQ_TOTAL:
842 sample->rawValue = mono_perfcounters->aspnet_requests;
851 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
854 int cat_id = GPOINTER_TO_INT (vtable->arg);
855 int id = cat_id >> 16;
858 case CATEGORY_ASPNET:
860 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
861 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
867 /* FIXME: we need to do this atomically */
871 /* this can be non-atomic */
879 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
881 const CounterDesc *cdesc;
883 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
885 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
886 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
888 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
894 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
896 CustomVTable *counter_data = (CustomVTable *)vtable;
898 fill_sample (sample);
899 sample->baseValue = 1;
901 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
903 sample->rawValue = 0;
905 sample->rawValue = *(guint64*)vtable->arg;
910 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
912 /* FIXME: check writability */
913 guint64 *ptr = vtable->arg;
916 /* FIXME: we need to do this atomically */
920 /* this can be non-atomic */
927 static SharedInstance*
928 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
930 SharedInstance* inst;
933 int size, data_offset;
935 inst = find_custom_instance (cat, instance);
938 name = mono_string_to_utf8 (instance);
939 size = sizeof (SharedInstance) + strlen (name);
943 size += (sizeof (guint64) * cat->num_counters);
945 ptr = shared_data_find_room (size);
951 inst = (SharedInstance*)ptr;
952 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
953 inst->header.size = size;
954 inst->category_offset = (char*)cat - (char*)shared_area;
955 cat->num_instances++;
956 /* now copy the variable data */
957 p = inst->instance_name;
959 p += strlen (name) + 1;
960 inst->header.ftype = FTYPE_INSTANCE;
968 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
970 CustomVTable* vtable;
971 vtable = g_new0 (CustomVTable, 1);
972 vtable->vtable.arg = data;
973 vtable->vtable.sample = custom_writable_counter;
974 vtable->vtable.update = custom_writable_update;
975 vtable->instance_desc = inst;
976 vtable->counter_desc = scounter;
978 return (ImplVtable*)vtable;
982 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
984 SharedCounter *scounter;
985 SharedInstance* inst;
988 scounter = find_custom_counter (cat, counter);
991 *type = simple_type_to_type [scounter->type];
992 inst = custom_get_instance (cat, scounter, instance);
995 size = sizeof (SharedInstance) + strlen (inst->instance_name);
998 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1001 static const CategoryDesc*
1002 find_category (MonoString *category)
1005 for (i = 0; i < NUM_CATEGORIES; ++i) {
1006 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1007 return &predef_categories [i];
1013 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1014 MonoString* machine, int *type, MonoBoolean *custom)
1016 const CategoryDesc *cdesc;
1017 /* no support for counters on other machines */
1018 if (mono_string_compare_ascii (machine, "."))
1020 cdesc = find_category (category);
1022 SharedCategory *scat = find_custom_category (category);
1026 return custom_get_impl (scat, counter, instance, type);
1028 switch (cdesc->id) {
1030 return cpu_get_impl (counter, instance, type, custom);
1032 return process_get_impl (counter, instance, type, custom);
1033 case CATEGORY_MONO_MEM:
1034 return mono_mem_get_impl (counter, instance, type, custom);
1038 case CATEGORY_REMOTING:
1039 case CATEGORY_LOADING:
1040 case CATEGORY_THREAD:
1041 case CATEGORY_INTEROP:
1042 case CATEGORY_SECURITY:
1043 case CATEGORY_ASPNET:
1044 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1050 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1052 ImplVtable *vtable = impl;
1053 if (vtable && vtable->sample)
1054 return vtable->sample (vtable, only_value, sample);
1059 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1061 ImplVtable *vtable = impl;
1062 if (vtable && vtable->update)
1063 return vtable->update (vtable, do_incr, value);
1068 mono_perfcounter_free_data (void *impl)
1070 ImplVtable *vtable = impl;
1071 if (vtable && vtable->cleanup)
1072 vtable->cleanup (vtable);
1076 /* Category icalls */
1078 mono_perfcounter_category_del (MonoString *name)
1080 const CategoryDesc *cdesc;
1081 SharedCategory *cat;
1082 cdesc = find_category (name);
1083 /* can't delete a predefined category */
1087 cat = find_custom_category (name);
1088 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1089 if (!cat || cat->num_instances) {
1093 cat->header.ftype = FTYPE_DELETED;
1099 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1101 const CategoryDesc *cdesc;
1102 /* no support for counters on other machines */
1103 if (mono_string_compare_ascii (machine, "."))
1105 cdesc = find_category (category);
1107 SharedCategory *scat = find_custom_category (category);
1110 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1112 return mono_string_new (mono_domain_get (), cdesc->help);
1116 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1117 * TRUE only if a counter with that name exists in the category.
1120 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1122 const CategoryDesc *cdesc;
1123 /* no support for counters on other machines */
1124 if (mono_string_compare_ascii (machine, "."))
1126 cdesc = find_category (category);
1128 SharedCategory *scat = find_custom_category (category);
1131 /* counter is allowed to be null */
1134 /* search through the custom category */
1135 return find_custom_counter (scat, counter) != NULL;
1137 /* counter is allowed to be null */
1140 if (get_counter_in_category (cdesc, counter))
1145 /* C map of the type with the same name */
1151 } CounterCreationData;
1154 * Since we'll keep a copy of the category per-process, we should also make sure
1155 * categories with the same name are compatible.
1158 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1162 int num_counters = mono_array_length (items);
1163 int counters_data_size;
1166 char **counter_info;
1169 SharedCategory *cat;
1171 /* FIXME: ensure there isn't a category created already */
1172 name = mono_string_to_utf8 (category);
1173 chelp = mono_string_to_utf8 (help);
1174 counter_info = g_new0 (char*, num_counters * 2);
1175 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1176 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1177 for (i = 0; i < num_counters; ++i) {
1178 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1179 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1180 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1181 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1183 for (i = 0; i < num_counters * 2; ++i) {
1184 if (!counter_info [i])
1186 size += strlen (counter_info [i]) + 1;
1190 counters_data_size = num_counters * 8; /* optimize for size later */
1194 ptr = shared_data_find_room (size);
1199 cat = (SharedCategory*)ptr;
1200 cat->header.extra = type;
1201 cat->header.size = size;
1202 cat->num_counters = num_counters;
1203 cat->counters_data_size = counters_data_size;
1204 /* now copy the vaiable data */
1207 p += strlen (name) + 1;
1209 p += strlen (chelp) + 1;
1210 for (i = 0; i < num_counters; ++i) {
1211 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1212 /* emit the SharedCounter structures */
1213 *p++ = perfctr_type_compress (data->type);
1215 strcpy (p, counter_info [i * 2]);
1216 p += strlen (counter_info [i * 2]) + 1;
1217 strcpy (p, counter_info [i * 2 + 1]);
1218 p += strlen (counter_info [i * 2 + 1]) + 1;
1220 cat->header.ftype = FTYPE_CATEGORY;
1225 for (i = 0; i < num_counters * 2; ++i) {
1226 g_free (counter_info [i]);
1228 g_free (counter_info);
1235 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1237 const CategoryDesc *cdesc;
1238 /* no support for counters on other machines */
1239 /*FIXME: machine appears to be wrong
1240 if (mono_string_compare_ascii (machine, "."))
1242 cdesc = find_category (category);
1244 SharedCategory *scat;
1245 scat = find_custom_category (category);
1248 if (find_custom_instance (scat, instance))
1251 /* FIXME: search instance */
1257 mono_perfcounter_category_names (MonoString *machine)
1261 MonoDomain *domain = mono_domain_get ();
1262 GSList *custom_categories, *tmp;
1263 /* no support for counters on other machines */
1264 if (mono_string_compare_ascii (machine, "."))
1265 return mono_array_new (domain, mono_get_string_class (), 0);
1267 custom_categories = get_custom_categories ();
1268 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1269 for (i = 0; i < NUM_CATEGORIES; ++i) {
1270 const CategoryDesc *cdesc = &predef_categories [i];
1271 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1273 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1274 SharedCategory *scat = tmp->data;
1275 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1279 g_slist_free (custom_categories);
1284 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1287 SharedCategory *scat;
1288 const CategoryDesc *cdesc;
1290 MonoDomain *domain = mono_domain_get ();
1291 /* no support for counters on other machines */
1292 if (mono_string_compare_ascii (machine, "."))
1293 return mono_array_new (domain, mono_get_string_class (), 0);
1294 cdesc = find_category (category);
1296 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1297 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1298 const CounterDesc *desc = &predef_counters [i];
1299 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1304 scat = find_custom_category (category);
1306 char *p = custom_category_counters (scat);
1308 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1309 for (i = 0; i < scat->num_counters; ++i) {
1310 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1311 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1312 p += strlen (p) + 1; /* skip counter help */
1318 return mono_array_new (domain, mono_get_string_class (), 0);
1322 get_string_array (void **array, int count, gboolean is_process)
1325 MonoDomain *domain = mono_domain_get ();
1326 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1327 for (i = 0; i < count; ++i) {
1331 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1332 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1334 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1337 mono_array_setref (res, i, mono_string_new (domain, p));
1345 get_mono_instances (void)
1354 buf = g_new (void*, count);
1355 res = mono_shared_area_instances (buf, count);
1356 } while (res == count);
1357 array = get_string_array (buf, res, TRUE);
1363 get_cpu_instances (void)
1368 count = mono_cpu_count ();
1369 buf = g_new (void*, count);
1370 for (i = 0; i < count; ++i)
1371 buf [i] = GINT_TO_POINTER (i);
1372 array = get_string_array (buf, count, FALSE);
1378 get_processes_instances (void)
1382 void **buf = mono_process_list (&count);
1384 return get_string_array (NULL, 0, FALSE);
1385 array = get_string_array (buf, count, TRUE);
1391 get_custom_instances (MonoString *category)
1393 SharedCategory *scat;
1394 scat = find_custom_category (category);
1396 GSList *list = get_custom_instances_list (scat);
1399 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1400 for (tmp = list; tmp; tmp = tmp->next) {
1401 SharedInstance *inst = tmp->data;
1402 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1405 g_slist_free (list);
1408 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1412 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1414 const CategoryDesc* cat;
1415 if (mono_string_compare_ascii (machine, "."))
1416 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1417 cat = find_category (category);
1419 return get_custom_instances (category);
1420 switch (cat->instance_type) {
1422 return get_mono_instances ();
1424 return get_cpu_instances ();
1425 case ProcessInstance:
1426 return get_processes_instances ();
1427 case ThreadInstance:
1429 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);