4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
19 #if defined (__OpenBSD__)
20 #include <sys/param.h>
22 #ifdef HAVE_SYS_TYPES_H
23 #include <sys/types.h>
25 #ifdef HAVE_SYS_TIME_H
28 #if defined (__NetBSD__) || defined (__APPLE__)
29 #include <sys/sysctl.h>
31 #include "metadata/mono-perfcounters.h"
32 #include "metadata/appdomain.h"
33 #include "metadata/object-internals.h"
35 #include "metadata/class-internals.h"
36 #include "utils/mono-time.h"
37 #include "utils/mono-mmap.h"
38 #include "utils/mono-proclib.h"
39 #include "utils/mono-networkinterfaces.h"
40 #include "utils/mono-error-internals.h"
41 #include "utils/atomic.h"
42 #include <mono/io-layer/io-layer.h>
44 /* map of CounterSample.cs */
45 struct _MonoCounterSample {
48 gint64 counterFrequency;
49 gint64 systemFrequency;
51 gint64 timeStamp100nSec;
52 gint64 counterTimeStamp;
56 #ifndef DISABLE_PERFCOUNTERS
57 /* map of PerformanceCounterType.cs */
59 NumberOfItemsHEX32=0x00000000,
60 NumberOfItemsHEX64=0x00000100,
61 NumberOfItems32=0x00010000,
62 NumberOfItems64=0x00010100,
63 CounterDelta32=0x00400400,
64 CounterDelta64=0x00400500,
65 SampleCounter=0x00410400,
66 CountPerTimeInterval32=0x00450400,
67 CountPerTimeInterval64=0x00450500,
68 RateOfCountsPerSecond32=0x10410400,
69 RateOfCountsPerSecond64=0x10410500,
70 RawFraction=0x20020400,
71 CounterTimer=0x20410500,
72 Timer100Ns=0x20510500,
73 SampleFraction=0x20C20400,
74 CounterTimerInverse=0x21410500,
75 Timer100NsInverse=0x21510500,
76 CounterMultiTimer=0x22410500,
77 CounterMultiTimer100Ns=0x22510500,
78 CounterMultiTimerInverse=0x23410500,
79 CounterMultiTimer100NsInverse=0x23510500,
80 AverageTimer32=0x30020400,
81 ElapsedTime=0x30240500,
82 AverageCount64=0x40020500,
83 SampleBase=0x40030401,
84 AverageBase=0x40030402,
86 CounterMultiBase=0x42030500
89 /* maps a small integer type to the counter types above */
91 simple_type_to_type [] = {
92 NumberOfItemsHEX32, NumberOfItemsHEX64,
93 NumberOfItems32, NumberOfItems64,
94 CounterDelta32, CounterDelta64,
95 SampleCounter, CountPerTimeInterval32,
96 CountPerTimeInterval64, RateOfCountsPerSecond32,
97 RateOfCountsPerSecond64, RawFraction,
98 CounterTimer, Timer100Ns,
99 SampleFraction, CounterTimerInverse,
100 Timer100NsInverse, CounterMultiTimer,
101 CounterMultiTimer100Ns, CounterMultiTimerInverse,
102 CounterMultiTimer100NsInverse, AverageTimer32,
103 ElapsedTime, AverageCount64,
104 SampleBase, AverageBase,
105 RawBase, CounterMultiBase
119 NetworkInterfaceInstance,
123 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
124 #define PERFCTR_COUNTER(id,name,help,type,field)
126 #include "mono-perfcounters-def.h"
131 #undef PERFCTR_COUNTER
132 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
133 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
134 /* each counter is assigned an id starting from 0 inside the category */
136 #include "mono-perfcounters-def.h"
141 #undef PERFCTR_COUNTER
142 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
143 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
144 /* this is used just to count the number of counters */
146 #include "mono-perfcounters-def.h"
150 static mono_mutex_t perfctr_mutex;
151 #define perfctr_lock() mono_mutex_lock (&perfctr_mutex)
152 #define perfctr_unlock() mono_mutex_unlock (&perfctr_mutex)
157 unsigned short counters_start;
158 unsigned short counters_size;
159 unsigned short data_start;
160 MonoPerfCounters counters;
165 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
167 struct stanza_header {
168 byte stanza_type; // FTYPE_*
170 ushort stanza_length; // includeas header
175 // perfcat and perfinstance are 4-bytes aligned
179 ushort length; // includes the counters
181 ushort counters_data_size;
183 char name[]; // null terminated
184 char help[]; // null terminated
185 // perfcounters follow
188 char name[]; // null terminated
189 char help[]; // null terminated
194 struct perfinstance {
196 byte data_offset; // offset of counters from beginning of struct
198 uint category_offset; // offset of category in the shared area
199 char name[]; // null terminated
200 // data follows: this is always 8-byte aligned
206 FTYPE_CATEGORY = 'C',
208 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
209 FTYPE_INSTANCE = 'I',
222 unsigned short num_counters;
223 unsigned short counters_data_size;
225 /* variable length data follows */
229 // SharedCounter counters_info [num_counters]
234 unsigned int category_offset;
235 /* variable length data follows */
236 char instance_name [1];
243 /* variable length data follows */
254 unsigned int instance_type : 6;
262 unsigned short offset; // offset inside MonoPerfCounters
267 #undef PERFCTR_COUNTER
268 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
269 #define PERFCTR_COUNTER(id,name,help,type,field)
270 static const CategoryDesc
271 predef_categories [] = {
272 #include "mono-perfcounters-def.h"
273 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
277 #undef PERFCTR_COUNTER
278 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
279 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
280 static const CounterDesc
281 predef_counters [] = {
282 #include "mono-perfcounters-def.h"
283 {NULL, NULL, -1, 0, 0}
287 * We have several different classes of counters:
289 * *) runtime counters
291 * *) user-defined counters
292 * *) windows counters (the implementation on windows will use this)
294 * To easily handle the differences we create a vtable for each class that contains the
295 * function pointers with the actual implementation to access the counters.
297 typedef struct _ImplVtable ImplVtable;
299 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
300 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
301 typedef void (*CleanupFunc) (ImplVtable *vtable);
317 MonoPerfCounters *counters;
323 SharedInstance *instance_desc;
324 SharedCounter *counter_desc;
328 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
330 ImplVtable *vtable = g_new0 (ImplVtable, 1);
332 vtable->sample = sample;
333 vtable->update = update;
337 MonoPerfCounters *mono_perfcounters = NULL;
338 static MonoSharedArea *shared_area = NULL;
345 /* maps a pid to a ExternalSArea pointer */
346 static GHashTable *pid_to_shared_area = NULL;
348 static MonoSharedArea *
349 load_sarea_for_pid (int pid)
352 MonoSharedArea *area = NULL;
355 if (pid_to_shared_area == NULL)
356 pid_to_shared_area = g_hash_table_new (NULL, NULL);
357 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
359 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
361 data = g_new (ExternalSArea, 1);
364 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
375 unref_pid_unlocked (int pid)
378 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
381 if (!data->refcount) {
382 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
383 mono_shared_area_unload (data->sarea);
390 predef_cleanup (ImplVtable *vtable)
392 PredefVtable *vt = (PredefVtable*)vtable;
393 /* ExternalSArea *data; */
396 if (!pid_to_shared_area) {
400 unref_pid_unlocked (vt->pid);
405 mono_determine_physical_ram_size (void)
407 #if defined (TARGET_WIN32)
408 MEMORYSTATUSEX memstat;
410 memstat.dwLength = sizeof (memstat);
411 GlobalMemoryStatusEx (&memstat);
412 return (guint64)memstat.ullTotalPhys;
413 #elif defined (__NetBSD__) || defined (__APPLE__)
427 size_t size_sys = sizeof (value);
429 sysctl (mib, 2, &value, &size_sys, NULL, 0);
433 return (guint64)value;
434 #elif defined (HAVE_SYSCONF)
435 guint64 page_size = 0, num_pages = 0;
437 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
438 * reports invalid values, please add your OS specific code below. */
440 page_size = (guint64)sysconf (_SC_PAGESIZE);
443 #ifdef _SC_PHYS_PAGES
444 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
447 if (!page_size || !num_pages) {
448 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
452 return page_size * num_pages;
459 mono_perfcounters_init (void)
461 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
465 mono_mutex_init_recursive (&perfctr_mutex);
467 shared_area = mono_shared_area ();
468 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
469 shared_area->counters_size = sizeof (MonoPerfCounters);
470 shared_area->data_start = d_offset;
471 shared_area->size = 4096;
472 mono_perfcounters = &shared_area->counters;
476 perfctr_type_compress (int type)
479 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
480 if (simple_type_to_type [i] == type)
483 /* NumberOfItems32 */
488 shared_data_reserve_room (int size, int ftype)
490 SharedHeader* header;
491 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
492 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
497 unsigned short *next;
498 if (*p == FTYPE_END) {
499 if (size < (end - p))
505 next = (unsigned short*)(p + 2);
506 if (*p == FTYPE_DELETED) {
507 /* we reuse only if it's the same size */
517 header = (SharedHeader*)p;
518 header->ftype = ftype;
519 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
525 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
528 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
531 unsigned short *next;
534 next = (unsigned short*)(p + 2);
535 if (!func ((SharedHeader*)p, data))
544 foreach_shared_item (SharedFunc func, void *data)
546 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
547 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
549 foreach_shared_item_in_area (p, end, func, data);
553 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
555 /* FIXME: make this case insensitive */
556 guint16 *strc = mono_string_chars (str);
557 while (*strc == *ascii_str++) {
562 return *strc - *(const unsigned char *)(ascii_str - 1);
571 category_search (SharedHeader *header, void *data)
573 CatSearch *search = data;
574 if (header->ftype == FTYPE_CATEGORY) {
575 SharedCategory *cat = (SharedCategory*)header;
576 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
584 static SharedCategory*
585 find_custom_category (MonoString *name)
590 foreach_shared_item (category_search, &search);
595 category_collect (SharedHeader *header, void *data)
597 GSList **list = data;
598 if (header->ftype == FTYPE_CATEGORY) {
599 *list = g_slist_prepend (*list, header);
605 get_custom_categories (void) {
607 foreach_shared_item (category_collect, &list);
612 custom_category_counters (SharedCategory* cat)
614 char *p = cat->name + strlen (cat->name) + 1;
615 p += strlen (p) + 1; /* skip category help */
619 static SharedCounter*
620 find_custom_counter (SharedCategory* cat, MonoString *name)
623 char *p = custom_category_counters (cat);
624 for (i = 0; i < cat->num_counters; ++i) {
625 SharedCounter *counter = (SharedCounter*)p;
626 if (mono_string_compare_ascii (name, counter->name) == 0)
628 p += 2; /* skip counter type */
629 p += strlen (p) + 1; /* skip counter name */
630 p += strlen (p) + 1; /* skip counter help */
636 unsigned int cat_offset;
639 SharedInstance* result;
644 instance_search (SharedHeader *header, void *data)
646 InstanceSearch *search = data;
647 if (header->ftype == FTYPE_INSTANCE) {
648 SharedInstance *ins = (SharedInstance*)header;
649 if (search->cat_offset == ins->category_offset) {
651 if (strcmp (search->name, ins->instance_name) == 0) {
652 search->result = ins;
656 search->list = g_slist_prepend (search->list, ins);
663 static SharedInstance*
664 find_custom_instance (SharedCategory* cat, char *name)
666 InstanceSearch search;
667 search.cat_offset = (char*)cat - (char*)shared_area;
671 search.result = NULL;
672 foreach_shared_item (instance_search, &search);
673 return search.result;
677 get_custom_instances_list (SharedCategory* cat)
679 InstanceSearch search;
680 search.cat_offset = (char*)cat - (char*)shared_area;
684 search.result = NULL;
685 foreach_shared_item (instance_search, &search);
690 custom_category_help (SharedCategory* cat)
692 return cat->name + strlen (cat->name) + 1;
695 static const CounterDesc*
696 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
698 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
699 const CounterDesc *end = &predef_counters [desc [1].first_counter];
700 for (; cdesc < end; ++cdesc) {
701 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
707 /* fill the info in sample (except the raw value) */
709 fill_sample (MonoCounterSample *sample)
711 sample->timeStamp = mono_100ns_ticks ();
712 sample->timeStamp100nSec = sample->timeStamp;
713 sample->counterTimeStamp = sample->timeStamp;
714 sample->counterFrequency = 10000000;
715 sample->systemFrequency = 10000000;
716 // the real basevalue needs to be get from a different counter...
717 sample->baseValue = 0;
721 id_from_string (MonoString *instance, gboolean is_process)
724 if (mono_string_length (instance)) {
725 char *id_str = mono_string_to_utf8 (instance);
727 id = strtol (id_str, &end, 0);
728 if (end == id_str && !is_process)
736 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
738 MonoProcessError error;
739 int id = GPOINTER_TO_INT (vtable->arg);
743 fill_sample (sample);
744 sample->baseValue = 1;
746 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
748 case COUNTER_CPU_USER_TIME:
749 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
751 case COUNTER_CPU_PRIV_TIME:
752 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
754 case COUNTER_CPU_INTR_TIME:
755 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
757 case COUNTER_CPU_DCP_TIME:
758 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
760 case COUNTER_CPU_PROC_TIME:
761 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
768 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
770 int id = id_from_string (instance, FALSE) << 5;
771 const CounterDesc *cdesc;
773 /* increase the shift above and the mask also in the implementation functions */
774 //g_assert (32 > desc [1].first_counter - desc->first_counter);
775 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
777 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
783 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
785 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
786 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
788 fill_sample (sample);
791 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
793 case COUNTER_NETWORK_BYTESRECSEC:
794 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
796 case COUNTER_NETWORK_BYTESSENTSEC:
797 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
799 case COUNTER_NETWORK_BYTESTOTALSEC:
800 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
804 if (error == MONO_NETWORK_ERROR_NONE)
811 network_cleanup (ImplVtable *vtable)
813 NetworkVtableArg *narg;
829 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
831 const CounterDesc *cdesc;
832 NetworkVtableArg *narg;
837 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
838 instance_name = mono_string_to_utf8 (instance);
839 narg = g_new0 (NetworkVtableArg, 1);
840 narg->id = cdesc->id;
841 narg->name = instance_name;
843 vtable = create_vtable (narg, get_network_counter, NULL);
844 vtable->cleanup = network_cleanup;
851 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
853 int id = GPOINTER_TO_INT (vtable->arg);
859 fill_sample (sample);
860 sample->baseValue = 1;
862 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
864 case COUNTER_PROC_USER_TIME:
865 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
867 case COUNTER_PROC_PRIV_TIME:
868 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
870 case COUNTER_PROC_PROC_TIME:
871 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
873 case COUNTER_PROC_THREADS:
874 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
876 case COUNTER_PROC_VBYTES:
877 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
879 case COUNTER_PROC_WSET:
880 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
882 case COUNTER_PROC_PBYTES:
883 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
890 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
892 int id = id_from_string (instance, TRUE) << 5;
893 const CounterDesc *cdesc;
895 /* increase the shift above and the mask also in the implementation functions */
896 //g_assert (32 > desc [1].first_counter - desc->first_counter);
897 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
899 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
905 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
907 int id = GPOINTER_TO_INT (vtable->arg);
909 fill_sample (sample);
910 sample->baseValue = 1;
912 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
914 case COUNTER_MEM_NUM_OBJECTS:
915 sample->rawValue = mono_stats.new_object_count;
917 case COUNTER_MEM_PHYS_TOTAL:
918 sample->rawValue = mono_determine_physical_ram_size ();;
925 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
927 const CounterDesc *cdesc;
929 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
931 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
937 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
939 PredefVtable *vt = (PredefVtable *)vtable;
940 const CounterDesc *desc;
941 int cat_id = GPOINTER_TO_INT (vtable->arg);
942 int id = cat_id >> 16;
945 fill_sample (sample);
946 sample->baseValue = 1;
948 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
949 sample->counterType = desc->type;
950 /* FIXME: check that the offset fits inside imported counters */
951 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
952 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
957 predef_vtable (void *arg, MonoString *instance)
959 MonoSharedArea *area;
960 PredefVtable *vtable;
961 char *pids = mono_string_to_utf8 (instance);
966 area = load_sarea_for_pid (pid);
970 vtable = g_new (PredefVtable, 1);
971 vtable->vtable.arg = arg;
972 vtable->vtable.sample = predef_readonly_counter;
973 vtable->vtable.cleanup = predef_cleanup;
974 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
977 return (ImplVtable*)vtable;
980 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
981 * this needs some way to set sample->counterType as well, though.
984 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
986 int cat_id = GPOINTER_TO_INT (vtable->arg);
987 int id = cat_id >> 16;
990 fill_sample (sample);
991 sample->baseValue = 1;
993 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
997 case COUNTER_EXC_THROWN:
998 sample->rawValue = mono_perfcounters->exceptions_thrown;
1002 case CATEGORY_ASPNET:
1004 case COUNTER_ASPNET_REQ_Q:
1005 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1007 case COUNTER_ASPNET_REQ_TOTAL:
1008 sample->rawValue = mono_perfcounters->aspnet_requests;
1012 case CATEGORY_THREADPOOL:
1014 case COUNTER_THREADPOOL_WORKITEMS:
1015 sample->rawValue = mono_perfcounters->threadpool_workitems;
1017 case COUNTER_THREADPOOL_IOWORKITEMS:
1018 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1020 case COUNTER_THREADPOOL_THREADS:
1021 sample->rawValue = mono_perfcounters->threadpool_threads;
1023 case COUNTER_THREADPOOL_IOTHREADS:
1024 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1030 case COUNTER_JIT_BYTES:
1031 sample->rawValue = mono_perfcounters->jit_bytes;
1033 case COUNTER_JIT_METHODS:
1034 sample->rawValue = mono_perfcounters->jit_methods;
1036 case COUNTER_JIT_TIME:
1037 sample->rawValue = mono_perfcounters->jit_time;
1039 case COUNTER_JIT_BYTES_PSEC:
1040 sample->rawValue = mono_perfcounters->jit_bytes;
1042 case COUNTER_JIT_FAILURES:
1043 sample->rawValue = mono_perfcounters->jit_failures;
1052 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1054 guint32 *volatile ptr = NULL;
1055 gint64 *volatile ptr64 = NULL;
1056 int cat_id = GPOINTER_TO_INT (vtable->arg);
1057 int id = cat_id >> 16;
1060 case CATEGORY_ASPNET:
1062 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1063 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1066 case CATEGORY_THREADPOOL:
1068 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1069 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1070 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1071 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1078 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1080 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1085 /* this can be non-atomic */
1090 /* FIXME: we need to do this atomically */
1091 /* No InterlockedIncrement64() yet */
1094 return InterlockedIncrement64 (ptr);
1096 return InterlockedDecrement64 (ptr);
1102 /* this can be non-atomic */
1110 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1112 const CounterDesc *cdesc;
1114 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1115 *type = cdesc->type;
1116 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1117 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1119 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1125 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1127 CustomVTable *counter_data = (CustomVTable *)vtable;
1129 fill_sample (sample);
1130 sample->baseValue = 1;
1132 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1134 sample->rawValue = 0;
1136 sample->rawValue = *(guint64*)vtable->arg;
1141 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1143 /* FIXME: check writability */
1144 guint64 *ptr = vtable->arg;
1147 /* FIXME: we need to do this atomically */
1151 /* this can be non-atomic */
1158 static SharedInstance*
1159 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1161 SharedInstance* inst;
1163 int size, data_offset;
1164 inst = find_custom_instance (cat, name);
1167 size = sizeof (SharedInstance) + strlen (name);
1171 size += (sizeof (guint64) * cat->num_counters);
1173 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1179 inst->category_offset = (char*)cat - (char*)shared_area;
1180 cat->num_instances++;
1181 /* now copy the variable data */
1182 p = inst->instance_name;
1184 p += strlen (name) + 1;
1191 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1193 CustomVTable* vtable;
1194 vtable = g_new0 (CustomVTable, 1);
1195 vtable->vtable.arg = data;
1196 vtable->vtable.sample = custom_writable_counter;
1197 vtable->vtable.update = custom_writable_update;
1198 vtable->instance_desc = inst;
1199 vtable->counter_desc = scounter;
1201 return (ImplVtable*)vtable;
1205 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1207 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1210 offset += scounter->seq_num * sizeof (guint64);
1211 return (char*)sinst + offset;
1215 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1217 SharedCounter *scounter;
1218 SharedInstance* inst;
1221 scounter = find_custom_counter (cat, counter);
1224 *type = simple_type_to_type [scounter->type];
1225 name = mono_string_to_utf8 (counter);
1226 inst = custom_get_instance (cat, scounter, name);
1230 return custom_vtable (scounter, inst, custom_get_value_address (scounter, inst));
1233 static const CategoryDesc*
1234 find_category (MonoString *category)
1237 for (i = 0; i < NUM_CATEGORIES; ++i) {
1238 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1239 return &predef_categories [i];
1245 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1246 MonoString* machine, int *type, MonoBoolean *custom)
1248 const CategoryDesc *cdesc;
1249 /* no support for counters on other machines */
1250 if (mono_string_compare_ascii (machine, "."))
1252 cdesc = find_category (category);
1254 SharedCategory *scat = find_custom_category (category);
1258 return custom_get_impl (scat, counter, instance, type);
1260 switch (cdesc->id) {
1262 return cpu_get_impl (counter, instance, type, custom);
1264 return process_get_impl (counter, instance, type, custom);
1265 case CATEGORY_MONO_MEM:
1266 return mono_mem_get_impl (counter, instance, type, custom);
1267 case CATEGORY_NETWORK:
1268 return network_get_impl (counter, instance, type, custom);
1272 case CATEGORY_REMOTING:
1273 case CATEGORY_LOADING:
1274 case CATEGORY_THREAD:
1275 case CATEGORY_INTEROP:
1276 case CATEGORY_SECURITY:
1277 case CATEGORY_ASPNET:
1278 case CATEGORY_THREADPOOL:
1279 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1285 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1287 ImplVtable *vtable = impl;
1288 if (vtable && vtable->sample)
1289 return vtable->sample (vtable, only_value, sample);
1294 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1296 ImplVtable *vtable = impl;
1297 if (vtable && vtable->update)
1298 return vtable->update (vtable, do_incr, value);
1303 mono_perfcounter_free_data (void *impl)
1305 ImplVtable *vtable = impl;
1306 if (vtable && vtable->cleanup)
1307 vtable->cleanup (vtable);
1311 /* Category icalls */
1313 mono_perfcounter_category_del (MonoString *name)
1315 const CategoryDesc *cdesc;
1316 SharedCategory *cat;
1317 cdesc = find_category (name);
1318 /* can't delete a predefined category */
1322 cat = find_custom_category (name);
1323 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1324 if (!cat || cat->num_instances) {
1328 cat->header.ftype = FTYPE_DELETED;
1334 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1336 const CategoryDesc *cdesc;
1337 /* no support for counters on other machines */
1338 if (mono_string_compare_ascii (machine, "."))
1340 cdesc = find_category (category);
1342 SharedCategory *scat = find_custom_category (category);
1345 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1347 return mono_string_new (mono_domain_get (), cdesc->help);
1351 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1352 * TRUE only if a counter with that name exists in the category.
1355 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1357 const CategoryDesc *cdesc;
1358 /* no support for counters on other machines */
1359 if (mono_string_compare_ascii (machine, "."))
1361 cdesc = find_category (category);
1363 SharedCategory *scat = find_custom_category (category);
1366 /* counter is allowed to be null */
1369 /* search through the custom category */
1370 return find_custom_counter (scat, counter) != NULL;
1372 /* counter is allowed to be null */
1375 if (get_counter_in_category (cdesc, counter))
1380 /* C map of the type with the same name */
1386 } CounterCreationData;
1389 * Since we'll keep a copy of the category per-process, we should also make sure
1390 * categories with the same name are compatible.
1393 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1398 int num_counters = mono_array_length (items);
1399 int counters_data_size;
1402 char **counter_info = NULL;
1404 SharedCategory *cat;
1406 /* FIXME: ensure there isn't a category created already */
1407 mono_error_init (&error);
1408 name = mono_string_to_utf8_checked (category, &error);
1409 if (!mono_error_ok (&error))
1411 chelp = mono_string_to_utf8_checked (help, &error);
1412 if (!mono_error_ok (&error))
1414 counter_info = g_new0 (char*, num_counters * 2);
1415 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1416 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1417 for (i = 0; i < num_counters; ++i) {
1418 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1419 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1420 if (!mono_error_ok (&error))
1422 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1423 if (!mono_error_ok (&error))
1425 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1427 for (i = 0; i < num_counters * 2; ++i) {
1428 if (!counter_info [i])
1430 size += strlen (counter_info [i]) + 1;
1434 counters_data_size = num_counters * 8; /* optimize for size later */
1438 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1443 cat->num_counters = num_counters;
1444 cat->counters_data_size = counters_data_size;
1445 /* now copy the vaiable data */
1448 p += strlen (name) + 1;
1450 p += strlen (chelp) + 1;
1451 for (i = 0; i < num_counters; ++i) {
1452 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1453 /* emit the SharedCounter structures */
1454 *p++ = perfctr_type_compress (data->type);
1456 strcpy (p, counter_info [i * 2]);
1457 p += strlen (counter_info [i * 2]) + 1;
1458 strcpy (p, counter_info [i * 2 + 1]);
1459 p += strlen (counter_info [i * 2 + 1]) + 1;
1466 for (i = 0; i < num_counters * 2; ++i) {
1467 g_free (counter_info [i]);
1469 g_free (counter_info);
1473 mono_error_cleanup (&error);
1478 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1480 const CategoryDesc *cdesc;
1481 SharedInstance *sinst;
1483 /* no support for counters on other machines */
1484 /*FIXME: machine appears to be wrong
1485 if (mono_string_compare_ascii (machine, "."))
1487 cdesc = find_category (category);
1489 SharedCategory *scat;
1490 scat = find_custom_category (category);
1493 name = mono_string_to_utf8 (instance);
1494 sinst = find_custom_instance (scat, name);
1499 /* FIXME: search instance */
1505 mono_perfcounter_category_names (MonoString *machine)
1509 MonoDomain *domain = mono_domain_get ();
1510 GSList *custom_categories, *tmp;
1511 /* no support for counters on other machines */
1512 if (mono_string_compare_ascii (machine, "."))
1513 return mono_array_new (domain, mono_get_string_class (), 0);
1515 custom_categories = get_custom_categories ();
1516 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1517 for (i = 0; i < NUM_CATEGORIES; ++i) {
1518 const CategoryDesc *cdesc = &predef_categories [i];
1519 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1521 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1522 SharedCategory *scat = tmp->data;
1523 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1527 g_slist_free (custom_categories);
1532 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1535 SharedCategory *scat;
1536 const CategoryDesc *cdesc;
1538 MonoDomain *domain = mono_domain_get ();
1539 /* no support for counters on other machines */
1540 if (mono_string_compare_ascii (machine, "."))
1541 return mono_array_new (domain, mono_get_string_class (), 0);
1542 cdesc = find_category (category);
1544 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1545 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1546 const CounterDesc *desc = &predef_counters [i];
1547 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1552 scat = find_custom_category (category);
1554 char *p = custom_category_counters (scat);
1556 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1557 for (i = 0; i < scat->num_counters; ++i) {
1558 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1559 p += 2; /* skip counter type */
1560 p += strlen (p) + 1; /* skip counter name */
1561 p += strlen (p) + 1; /* skip counter help */
1567 return mono_array_new (domain, mono_get_string_class (), 0);
1571 get_string_array (void **array, int count, gboolean is_process)
1574 MonoDomain *domain = mono_domain_get ();
1575 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1576 for (i = 0; i < count; ++i) {
1580 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1581 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1583 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1586 mono_array_setref (res, i, mono_string_new (domain, p));
1594 get_string_array_of_strings (void **array, int count)
1597 MonoDomain *domain = mono_domain_get ();
1598 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1599 for (i = 0; i < count; ++i) {
1601 mono_array_setref (res, i, mono_string_new (domain, p));
1608 get_mono_instances (void)
1617 buf = g_new (void*, count);
1618 res = mono_shared_area_instances (buf, count);
1619 } while (res == count);
1620 array = get_string_array (buf, res, TRUE);
1626 get_cpu_instances (void)
1632 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1633 buf = g_new (void*, count);
1634 for (i = 0; i < count; ++i)
1635 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1636 array = get_string_array (buf, count, FALSE);
1638 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1643 get_processes_instances (void)
1647 void **buf = mono_process_list (&count);
1649 return get_string_array (NULL, 0, FALSE);
1650 array = get_string_array (buf, count, TRUE);
1656 get_networkinterface_instances (void)
1660 void **buf = mono_networkinterface_list (&count);
1662 return get_string_array_of_strings (NULL, 0);
1663 array = get_string_array_of_strings (buf, count);
1664 g_strfreev ((char **) buf);
1669 get_custom_instances (MonoString *category)
1671 SharedCategory *scat;
1672 scat = find_custom_category (category);
1674 GSList *list = get_custom_instances_list (scat);
1677 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1678 for (tmp = list; tmp; tmp = tmp->next) {
1679 SharedInstance *inst = tmp->data;
1680 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1683 g_slist_free (list);
1686 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1690 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1692 const CategoryDesc* cat;
1693 if (mono_string_compare_ascii (machine, "."))
1694 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1695 cat = find_category (category);
1697 return get_custom_instances (category);
1698 switch (cat->instance_type) {
1700 return get_mono_instances ();
1702 return get_cpu_instances ();
1703 case ProcessInstance:
1704 return get_processes_instances ();
1705 case NetworkInterfaceInstance:
1706 return get_networkinterface_instances ();
1707 case ThreadInstance:
1709 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1714 PerfCounterEnumCallback cb;
1716 } PerfCounterForeachData;
1719 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1722 char *p, *name, *help;
1726 SharedCategory *cat;
1727 SharedCounter *counter;
1728 SharedInstance *inst;
1729 PerfCounterForeachData *foreach_data = data;
1731 if (header->ftype == FTYPE_CATEGORY) {
1732 cat = (SharedCategory*)header;
1735 p += strlen (p) + 1; /* skip category name */
1736 p += strlen (p) + 1; /* skip category help */
1738 for (i = 0; i < cat->num_counters; ++i) {
1739 counter = (SharedCounter*) p;
1740 type = (unsigned char)*p++;
1741 seq_num = (int)*p++;
1743 p += strlen (p) + 1;
1745 p += strlen (p) + 1;
1747 inst = custom_get_instance (cat, counter, name);
1750 addr = custom_get_value_address (counter, inst);
1751 if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1760 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1762 PerfCounterForeachData foreach_data = { cb, data };
1766 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1773 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1775 g_assert_not_reached ();
1779 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1781 g_assert_not_reached ();
1785 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1787 g_assert_not_reached ();
1791 mono_perfcounter_free_data (void *impl)
1793 g_assert_not_reached ();
1796 /* Category icalls */
1798 mono_perfcounter_category_del (MonoString *name)
1800 g_assert_not_reached ();
1804 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1806 g_assert_not_reached ();
1810 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1812 g_assert_not_reached ();
1816 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1818 g_assert_not_reached ();
1822 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1824 g_assert_not_reached ();
1828 mono_perfcounter_category_names (MonoString *machine)
1830 g_assert_not_reached ();
1834 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1836 g_assert_not_reached ();
1840 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1842 g_assert_not_reached ();