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 <mono/io-layer/io-layer.h>
26 /* map of CounterSample.cs */
27 struct _MonoCounterSample {
30 gint64 counterFrequency;
31 gint64 systemFrequency;
33 gint64 timeStamp100nSec;
34 gint64 counterTimeStamp;
38 /* map of PerformanceCounterType.cs */
40 NumberOfItemsHEX32=0x00000000,
41 NumberOfItemsHEX64=0x00000100,
42 NumberOfItems32=0x00010000,
43 NumberOfItems64=0x00010100,
44 CounterDelta32=0x00400400,
45 CounterDelta64=0x00400500,
46 SampleCounter=0x00410400,
47 CountPerTimeInterval32=0x00450400,
48 CountPerTimeInterval64=0x00450500,
49 RateOfCountsPerSecond32=0x10410400,
50 RateOfCountsPerSecond64=0x10410500,
51 RawFraction=0x20020400,
52 CounterTimer=0x20410500,
53 Timer100Ns=0x20510500,
54 SampleFraction=0x20C20400,
55 CounterTimerInverse=0x21410500,
56 Timer100NsInverse=0x21510500,
57 CounterMultiTimer=0x22410500,
58 CounterMultiTimer100Ns=0x22510500,
59 CounterMultiTimerInverse=0x23410500,
60 CounterMultiTimer100NsInverse=0x23510500,
61 AverageTimer32=0x30020400,
62 ElapsedTime=0x30240500,
63 AverageCount64=0x40020500,
64 SampleBase=0x40030401,
65 AverageBase=0x40030402,
67 CounterMultiBase=0x42030500
70 /* maps a small integer type to the counter types above */
72 simple_type_to_type [] = {
73 NumberOfItemsHEX32, NumberOfItemsHEX64,
74 NumberOfItems32, NumberOfItems64,
75 CounterDelta32, CounterDelta64,
76 SampleCounter, CountPerTimeInterval32,
77 CountPerTimeInterval64, RateOfCountsPerSecond32,
78 RateOfCountsPerSecond64, RawFraction,
79 CounterTimer, Timer100Ns,
80 SampleFraction, CounterTimerInverse,
81 Timer100NsInverse, CounterMultiTimer,
82 CounterMultiTimer100Ns, CounterMultiTimerInverse,
83 CounterMultiTimer100NsInverse, AverageTimer32,
84 ElapsedTime, AverageCount64,
85 SampleBase, AverageBase,
86 RawBase, CounterMultiBase
103 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
104 #define PERFCTR_COUNTER(id,name,help,type)
106 #include "mono-perfcounters-def.h"
111 #undef PERFCTR_COUNTER
112 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
113 #define PERFCTR_COUNTER(id,name,help,type) COUNTER_ ## id,
114 /* each counter is assigned an id starting from 0 inside the category */
116 #include "mono-perfcounters-def.h"
121 #undef PERFCTR_COUNTER
122 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
123 #define PERFCTR_COUNTER(id,name,help,type) CCOUNTER_ ## id,
124 /* this is used just to count the number of counters */
126 #include "mono-perfcounters-def.h"
130 static CRITICAL_SECTION perfctr_mutex;
131 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
132 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
137 unsigned short counters_start;
138 unsigned short counters_size;
139 unsigned short data_start;
140 MonoPerfCounters counters;
145 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
147 struct stanza_header {
148 byte stanza_type; // FTYPE_*
150 ushort stanza_length; // includeas header
155 // perfcat and perfinstance are 4-bytes aligned
159 ushort length; // includes the counters
161 ushort counters_data_size;
163 char name[]; // null terminated
164 char help[]; // null terminated
165 // perfcounters follow
168 char name[]; // null terminated
169 char help[]; // null terminated
174 struct perfinstance {
176 byte data_offset; // offset of counters from beginning of struct
178 uint category_offset; // offset of category in the shared area
179 char name[]; // null terminated
180 // data follows: this is always 8-byte aligned
186 FTYPE_CATEGORY = 'C',
188 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
189 FTYPE_INSTANCE = 'I',
202 unsigned short num_counters;
203 unsigned short counters_data_size;
205 /* variable length data follows */
211 unsigned int category_offset;
212 /* variable length data follows */
218 /* variable length data follows */
227 unsigned int instance_type : 6;
239 #undef PERFCTR_COUNTER
240 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
241 #define PERFCTR_COUNTER(id,name,help,type)
242 static const CategoryDesc
243 predef_categories [] = {
244 #include "mono-perfcounters-def.h"
245 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
249 #undef PERFCTR_COUNTER
250 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
251 #define PERFCTR_COUNTER(id,name,help,type) {name, help, COUNTER_ ## id, type},
252 static const CounterDesc
253 predef_counters [] = {
254 #include "mono-perfcounters-def.h"
259 * We have several different classes of counters:
261 * *) runtime counters
263 * *) user-defined counters
264 * *) windows counters (the implementation on windows will use this)
266 * To easily handle the differences we create a vtable for each class that contains the
267 * function pointers with the actual implementation to access the counters.
269 typedef struct _ImplVtable ImplVtable;
271 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
272 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
273 typedef void (*CleanupFunc) (ImplVtable *vtable);
284 MonoPerfCounters *counters;
289 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
291 ImplVtable *vtable = g_new0 (ImplVtable, 1);
293 vtable->sample = sample;
294 vtable->update = update;
298 MonoPerfCounters *mono_perfcounters = NULL;
299 static MonoSharedArea *shared_area = NULL;
306 /* maps a pid to a ExternalSArea pointer */
307 static GHashTable *pid_to_shared_area = NULL;
309 static MonoSharedArea *
310 load_sarea_for_pid (int pid)
313 MonoSharedArea *area = NULL;
316 if (pid_to_shared_area == NULL)
317 pid_to_shared_area = g_hash_table_new (NULL, NULL);
318 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
320 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
322 data = g_new (ExternalSArea, 1);
325 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
336 predef_cleanup (ImplVtable *vtable)
338 PredefVtable *vt = (PredefVtable*)vtable;
341 if (!pid_to_shared_area) {
345 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
348 if (!data->refcount) {
349 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
350 mono_shared_area_unload (data->sarea);
358 mono_perfcounters_init (void)
360 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
364 InitializeCriticalSection (&perfctr_mutex);
366 shared_area = mono_shared_area ();
367 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
368 shared_area->counters_size = sizeof (MonoPerfCounters);
369 shared_area->data_start = d_offset;
370 shared_area->size = 4096;
371 mono_perfcounters = &shared_area->counters;
375 perfctr_type_compress (int type)
378 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
379 if (simple_type_to_type [i] == type)
382 /* NumberOfItems32 */
386 static unsigned char*
387 shared_data_find_room (int size)
389 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
390 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
395 unsigned short *next;
396 if (*p == FTYPE_END) {
397 if (size < (end - p))
403 next = (unsigned short*)(p + 2);
404 if (*p == FTYPE_DELETED) {
405 /* we reuse only if it's the same size */
415 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
418 foreach_shared_item (SharedFunc func, void *data)
420 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
421 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
424 unsigned short *next;
427 next = (unsigned short*)(p + 2);
428 if (!func ((SharedHeader*)p, data))
437 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
439 /* FIXME: make this case insensitive */
440 guint16 *strc = mono_string_chars (str);
441 while (*strc == *ascii_str++) {
446 return *strc - *(const unsigned char *)(ascii_str - 1);
455 category_search (SharedHeader *header, void *data)
457 CatSearch *search = data;
458 if (header->ftype == FTYPE_CATEGORY) {
459 SharedCategory *cat = (SharedCategory*)header;
460 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
468 static SharedCategory*
469 find_custom_category (MonoString *name)
474 foreach_shared_item (category_search, &search);
479 category_collect (SharedHeader *header, void *data)
481 GSList **list = data;
482 if (header->ftype == FTYPE_CATEGORY) {
483 *list = g_slist_prepend (*list, header);
489 get_custom_categories (void) {
491 foreach_shared_item (category_collect, &list);
496 custom_category_counters (SharedCategory* cat)
498 char *p = cat->name + strlen (cat->name) + 1;
499 p += strlen (p) + 1; /* skip category help */
503 static SharedCounter*
504 find_custom_counter (SharedCategory* cat, MonoString *name)
507 char *p = custom_category_counters (cat);
508 for (i = 0; i < cat->num_counters; ++i) {
509 SharedCounter *counter = (SharedCounter*)p;
510 if (mono_string_compare_ascii (name, counter->name) == 0)
512 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
513 p += strlen (p) + 1; /* skip counter help */
519 custom_category_help (SharedCategory* cat)
521 return cat->name + strlen (cat->name) + 1;
524 static const CounterDesc*
525 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
527 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
528 const CounterDesc *end = &predef_counters [desc [1].first_counter];
529 for (; cdesc < end; ++cdesc) {
530 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
536 /* fill the info in sample (except the raw value) */
538 fill_sample (MonoCounterSample *sample)
540 sample->timeStamp = mono_100ns_ticks ();
541 sample->timeStamp100nSec = sample->timeStamp;
542 sample->counterTimeStamp = sample->timeStamp;
543 sample->counterFrequency = 10000000;
544 sample->systemFrequency = 10000000;
545 // the real basevalue needs to be get from a different counter...
546 sample->baseValue = 0;
550 id_from_string (MonoString *instance)
553 if (mono_string_length (instance)) {
554 char *id_str = mono_string_to_utf8 (instance);
556 id = strtol (id_str, &end, 0);
565 get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
569 int hz = 100 * 2; // 2 numprocs
570 long long unsigned int user_ticks, nice_ticks, system_ticks, idle_ticks, iowait_ticks, irq_ticks, sirq_ticks;
571 FILE *f = fopen ("/proc/stat", "r");
574 while ((s = fgets (buf, sizeof (buf), f))) {
576 if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
578 } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
585 sscanf (data, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_ticks, &nice_ticks, &system_ticks, &idle_ticks, &iowait_ticks, &irq_ticks, &sirq_ticks);
590 *user = (user_ticks + nice_ticks) * 10000000 / hz;
592 *systemt = (system_ticks) * 10000000 / hz;
594 *irq = (irq_ticks) * 10000000 / hz;
596 *sirq = (sirq_ticks) * 10000000 / hz;
598 *idle = (idle_ticks) * 10000000 / hz;
602 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
605 int id = GPOINTER_TO_INT (vtable->arg);
609 fill_sample (sample);
610 sample->baseValue = 1;
612 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
614 case COUNTER_CPU_USER_TIME:
615 get_cpu_times (pid, &value, NULL, NULL, NULL, NULL);
616 sample->rawValue = value;
618 case COUNTER_CPU_PRIV_TIME:
619 get_cpu_times (pid, NULL, &value, NULL, NULL, NULL);
620 sample->rawValue = value;
622 case COUNTER_CPU_INTR_TIME:
623 get_cpu_times (pid, NULL, NULL, &value, NULL, NULL);
624 sample->rawValue = value;
626 case COUNTER_CPU_DCP_TIME:
627 get_cpu_times (pid, NULL, NULL, NULL, &value, NULL);
628 sample->rawValue = value;
630 case COUNTER_CPU_PROC_TIME:
631 get_cpu_times (pid, NULL, NULL, NULL, NULL, &value);
632 sample->rawValue = value;
639 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
641 int id = id_from_string (instance) << 5;
642 const CounterDesc *cdesc;
644 /* increase the shift above and the mask also in the implementation functions */
645 //g_assert (32 > desc [1].first_counter - desc->first_counter);
646 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
648 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
654 * /proc/pid/stat format:
656 * [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
657 * [10] utime stime cutime cstime prio nice threads start_time vsize rss
658 * [20] rsslim start_code end_code start_stack esp eip pending blocked sigign sigcatch
659 * [30] wchan 0 0 exit_signal cpu rt_prio policy
663 get_process_time (int pid, int pos, int sum)
671 g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
672 f = fopen (buf, "r");
675 len = fread (buf, 1, sizeof (buf), f);
679 s = strchr (buf, ')');
683 while (g_ascii_isspace (*s)) s++;
686 /* skip the status char */
687 while (*s && !g_ascii_isspace (*s)) s++;
690 for (i = 0; i < pos; ++i) {
691 while (g_ascii_isspace (*s)) s++;
694 while (*s && !g_ascii_isspace (*s)) s++;
698 /* we are finally at the needed item */
699 value = strtoul (s, &end, 0);
700 /* add also the following value */
702 while (g_ascii_isspace (*s)) s++;
705 value += strtoul (s, &end, 0);
711 get_pid_stat_item (int pid, const char *item)
716 int len = strlen (item);
718 g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
719 f = fopen (buf, "r");
722 while ((s = fgets (buf, sizeof (buf), f))) {
725 if (strncmp (buf, item, len))
727 if (buf [len] != ':')
730 return atoi (buf + len + 1);
737 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
739 int id = GPOINTER_TO_INT (vtable->arg);
745 fill_sample (sample);
746 sample->baseValue = 1;
748 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
750 case COUNTER_PROC_USER_TIME:
751 sample->rawValue = get_process_time (pid, 12, FALSE);
753 case COUNTER_PROC_PRIV_TIME:
754 sample->rawValue = get_process_time (pid, 13, FALSE);
756 case COUNTER_PROC_PROC_TIME:
757 sample->rawValue = get_process_time (pid, 12, TRUE);
759 case COUNTER_PROC_THREADS:
760 sample->rawValue = get_pid_stat_item (pid, "Threads");
762 case COUNTER_PROC_VBYTES:
763 sample->rawValue = get_pid_stat_item (pid, "VmSize") * 1024;
765 case COUNTER_PROC_WSET:
766 sample->rawValue = get_pid_stat_item (pid, "VmRSS") * 1024;
768 case COUNTER_PROC_PBYTES:
769 sample->rawValue = get_pid_stat_item (pid, "VmData") * 1024;
776 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
778 int id = id_from_string (instance) << 5;
779 const CounterDesc *cdesc;
781 /* increase the shift above and the mask also in the implementation functions */
782 //g_assert (32 > desc [1].first_counter - desc->first_counter);
783 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
785 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
791 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
793 int id = GPOINTER_TO_INT (vtable->arg);
795 fill_sample (sample);
796 sample->baseValue = 1;
798 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
800 case COUNTER_MEM_NUM_OBJECTS:
801 sample->rawValue = mono_stats.new_object_count;
808 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
810 const CounterDesc *cdesc;
812 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
814 return create_vtable (GINT_TO_POINTER (cdesc->id), mono_mem_counter, NULL);
820 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
822 PredefVtable *vt = (PredefVtable *)vtable;
823 int cat_id = GPOINTER_TO_INT (vtable->arg);
824 int id = cat_id >> 16;
827 fill_sample (sample);
828 sample->baseValue = 1;
830 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
834 case COUNTER_JIT_BYTES:
835 case COUNTER_JIT_BYTES_PSEC:
836 sample->rawValue = vt->counters->jit_bytes;
838 case COUNTER_JIT_METHODS:
839 sample->rawValue = vt->counters->jit_methods;
845 case COUNTER_EXC_THROWN:
846 sample->rawValue = vt->counters->exceptions_thrown;
851 case CATEGORY_ASPNET:
853 case COUNTER_ASPNET_REQ_Q:
854 sample->rawValue = vt->counters->aspnet_requests_queued;
862 predef_vtable (void *arg, MonoString *instance)
864 MonoSharedArea *area;
865 PredefVtable *vtable;
866 char *pids = mono_string_to_utf8 (instance);
871 area = load_sarea_for_pid (pid);
875 vtable = g_new (PredefVtable, 1);
876 vtable->vtable.arg = arg;
877 vtable->vtable.sample = predef_readonly_counter;
878 vtable->vtable.cleanup = predef_cleanup;
879 vtable->counters = &area->counters;
882 return (ImplVtable*)vtable;
885 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
886 * this needs some way to set sample->counterType as well, though.
889 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
891 int cat_id = GPOINTER_TO_INT (vtable->arg);
892 int id = cat_id >> 16;
895 fill_sample (sample);
896 sample->baseValue = 1;
898 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
900 case CATEGORY_ASPNET:
902 case COUNTER_ASPNET_REQ_Q:
903 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
912 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
915 int cat_id = GPOINTER_TO_INT (vtable->arg);
916 int id = cat_id >> 16;
919 case CATEGORY_ASPNET:
921 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
927 /* FIXME: we need to do this atomically */
931 /* this can be non-atomic */
939 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
941 const CounterDesc *cdesc;
943 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
945 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
946 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
948 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
954 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
956 SharedCounter *scounter = vtable->arg;
958 fill_sample (sample);
959 sample->baseValue = 1;
961 sample->counterType = simple_type_to_type [scounter->type];
963 sample->rawValue = 0;
968 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
974 /* FIXME: we need to do this atomically */
978 /* this can be non-atomic */
986 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
988 SharedCounter *scounter;
990 scounter = find_custom_counter (cat, counter);
993 *type = simple_type_to_type [scounter->type];
994 /* FIXME: use instance */
995 return create_vtable (scounter, custom_writable_counter, custom_writable_update);
998 static const CategoryDesc*
999 find_category (MonoString *category)
1002 for (i = 0; i < NUM_CATEGORIES; ++i) {
1003 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1004 return &predef_categories [i];
1010 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1011 MonoString* machine, int *type, MonoBoolean *custom)
1013 const CategoryDesc *cdesc;
1014 /* no support for counters on other machines */
1015 if (mono_string_compare_ascii (machine, "."))
1017 cdesc = find_category (category);
1019 SharedCategory *scat = find_custom_category (category);
1023 return custom_get_impl (scat, counter, instance, type);
1025 switch (cdesc->id) {
1027 return cpu_get_impl (counter, instance, type, custom);
1029 return process_get_impl (counter, instance, type, custom);
1030 case CATEGORY_MONO_MEM:
1031 return mono_mem_get_impl (counter, instance, type, custom);
1034 case CATEGORY_ASPNET:
1035 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1041 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1043 ImplVtable *vtable = impl;
1044 if (vtable && vtable->sample)
1045 return vtable->sample (vtable, only_value, sample);
1050 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1052 ImplVtable *vtable = impl;
1053 if (vtable && vtable->update)
1054 return vtable->update (vtable, do_incr, value);
1059 mono_perfcounter_free_data (void *impl)
1061 ImplVtable *vtable = impl;
1062 if (vtable && vtable->cleanup)
1063 vtable->cleanup (vtable);
1067 /* Category icalls */
1069 mono_perfcounter_category_del (MonoString *name)
1071 const CategoryDesc *cdesc;
1072 SharedCategory *cat;
1073 cdesc = find_category (name);
1074 /* can't delete a predefined category */
1078 cat = find_custom_category (name);
1079 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1080 if (!cat || cat->num_instances) {
1084 cat->header.ftype = FTYPE_DELETED;
1090 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1092 const CategoryDesc *cdesc;
1093 /* no support for counters on other machines */
1094 if (mono_string_compare_ascii (machine, "."))
1096 cdesc = find_category (category);
1098 SharedCategory *scat = find_custom_category (category);
1101 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1103 return mono_string_new (mono_domain_get (), cdesc->help);
1107 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1108 * TRUE only if a counter with that name exists in the category.
1111 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1113 const CategoryDesc *cdesc;
1114 /* no support for counters on other machines */
1115 if (mono_string_compare_ascii (machine, "."))
1117 cdesc = find_category (category);
1119 SharedCategory *scat = find_custom_category (category);
1122 /* counter is allowed to be null */
1125 /* search through the custom category */
1126 return find_custom_counter (scat, counter) != NULL;
1128 /* counter is allowed to be null */
1131 if (get_counter_in_category (cdesc, counter))
1136 /* C map of the type with the same name */
1142 } CounterCreationData;
1145 * Since we'll keep a copy of the category per-process, we should also make sure
1146 * categories with the same name are compatible.
1149 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1153 int num_counters = mono_array_length (items);
1154 int counters_data_size;
1155 char *name = mono_string_to_utf8 (category);
1156 char *chelp = mono_string_to_utf8 (help);
1157 char **counter_info;
1160 SharedCategory *cat;
1162 counter_info = g_new0 (char*, num_counters * 2);
1163 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1164 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1165 for (i = 0; i < num_counters; ++i) {
1166 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1167 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1168 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1169 size += 3; /* type and two 0 string terminators */
1171 for (i = 0; i < num_counters * 2; ++i) {
1172 if (!counter_info [i])
1174 size += strlen (counter_info [i]);
1176 counters_data_size = num_counters * 8; /* optimize for size later */
1180 ptr = shared_data_find_room (size);
1185 cat = (SharedCategory*)ptr;
1186 cat->header.extra = type;
1187 cat->header.size = size;
1188 cat->num_counters = num_counters;
1189 cat->counters_data_size = counters_data_size;
1190 /* now copy the vaiable data */
1193 p += strlen (name) + 1;
1195 p += strlen (chelp) + 1;
1196 for (i = 0; i < num_counters; ++i) {
1197 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1198 *p++ = perfctr_type_compress (data->type);
1199 strcpy (p, counter_info [i * 2]);
1200 p += strlen (counter_info [i * 2]) + 1;
1201 strcpy (p, counter_info [i * 2 + 1]);
1202 p += strlen (counter_info [i * 2 + 1]) + 1;
1204 cat->header.ftype = FTYPE_CATEGORY;
1209 for (i = 0; i < num_counters * 2; ++i) {
1210 g_free (counter_info [i]);
1212 g_free (counter_info);
1219 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1221 const CategoryDesc *cdesc;
1222 /* no support for counters on other machines */
1223 if (mono_string_compare_ascii (machine, "."))
1225 cdesc = find_category (category);
1232 mono_perfcounter_category_names (MonoString *machine)
1236 MonoDomain *domain = mono_domain_get ();
1237 GSList *custom_categories, *tmp;
1238 /* no support for counters on other machines */
1239 if (mono_string_compare_ascii (machine, "."))
1240 return mono_array_new (domain, mono_get_string_class (), 0);
1242 custom_categories = get_custom_categories ();
1243 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1244 for (i = 0; i < NUM_CATEGORIES; ++i) {
1245 const CategoryDesc *cdesc = &predef_categories [i];
1246 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1248 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1249 SharedCategory *scat = tmp->data;
1250 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1254 g_slist_free (custom_categories);
1259 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1262 SharedCategory *scat;
1263 const CategoryDesc *cdesc;
1265 MonoDomain *domain = mono_domain_get ();
1266 /* no support for counters on other machines */
1267 if (mono_string_compare_ascii (machine, "."))
1268 return mono_array_new (domain, mono_get_string_class (), 0);
1269 cdesc = find_category (category);
1271 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1272 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1273 const CounterDesc *desc = &predef_counters [i];
1274 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1279 scat = find_custom_category (category);
1281 char *p = custom_category_counters (scat);
1283 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1284 for (i = 0; i < scat->num_counters; ++i) {
1285 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1286 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1287 p += strlen (p) + 1; /* skip counter help */
1293 return mono_array_new (domain, mono_get_string_class (), 0);
1297 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1299 if (mono_string_compare_ascii (machine, "."))
1300 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1301 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);