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 <mono/io-layer/io-layer.h>
43 /* map of CounterSample.cs */
44 struct _MonoCounterSample {
47 gint64 counterFrequency;
48 gint64 systemFrequency;
50 gint64 timeStamp100nSec;
51 gint64 counterTimeStamp;
55 #ifndef DISABLE_PERFCOUNTERS
56 /* map of PerformanceCounterType.cs */
58 NumberOfItemsHEX32=0x00000000,
59 NumberOfItemsHEX64=0x00000100,
60 NumberOfItems32=0x00010000,
61 NumberOfItems64=0x00010100,
62 CounterDelta32=0x00400400,
63 CounterDelta64=0x00400500,
64 SampleCounter=0x00410400,
65 CountPerTimeInterval32=0x00450400,
66 CountPerTimeInterval64=0x00450500,
67 RateOfCountsPerSecond32=0x10410400,
68 RateOfCountsPerSecond64=0x10410500,
69 RawFraction=0x20020400,
70 CounterTimer=0x20410500,
71 Timer100Ns=0x20510500,
72 SampleFraction=0x20C20400,
73 CounterTimerInverse=0x21410500,
74 Timer100NsInverse=0x21510500,
75 CounterMultiTimer=0x22410500,
76 CounterMultiTimer100Ns=0x22510500,
77 CounterMultiTimerInverse=0x23410500,
78 CounterMultiTimer100NsInverse=0x23510500,
79 AverageTimer32=0x30020400,
80 ElapsedTime=0x30240500,
81 AverageCount64=0x40020500,
82 SampleBase=0x40030401,
83 AverageBase=0x40030402,
85 CounterMultiBase=0x42030500
88 /* maps a small integer type to the counter types above */
90 simple_type_to_type [] = {
91 NumberOfItemsHEX32, NumberOfItemsHEX64,
92 NumberOfItems32, NumberOfItems64,
93 CounterDelta32, CounterDelta64,
94 SampleCounter, CountPerTimeInterval32,
95 CountPerTimeInterval64, RateOfCountsPerSecond32,
96 RateOfCountsPerSecond64, RawFraction,
97 CounterTimer, Timer100Ns,
98 SampleFraction, CounterTimerInverse,
99 Timer100NsInverse, CounterMultiTimer,
100 CounterMultiTimer100Ns, CounterMultiTimerInverse,
101 CounterMultiTimer100NsInverse, AverageTimer32,
102 ElapsedTime, AverageCount64,
103 SampleBase, AverageBase,
104 RawBase, CounterMultiBase
118 NetworkInterfaceInstance,
122 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
123 #define PERFCTR_COUNTER(id,name,help,type,field)
125 #include "mono-perfcounters-def.h"
130 #undef PERFCTR_COUNTER
131 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
132 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
133 /* each counter is assigned an id starting from 0 inside the category */
135 #include "mono-perfcounters-def.h"
140 #undef PERFCTR_COUNTER
141 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
142 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
143 /* this is used just to count the number of counters */
145 #include "mono-perfcounters-def.h"
149 static CRITICAL_SECTION perfctr_mutex;
150 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
151 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
156 unsigned short counters_start;
157 unsigned short counters_size;
158 unsigned short data_start;
159 MonoPerfCounters counters;
164 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
166 struct stanza_header {
167 byte stanza_type; // FTYPE_*
169 ushort stanza_length; // includeas header
174 // perfcat and perfinstance are 4-bytes aligned
178 ushort length; // includes the counters
180 ushort counters_data_size;
182 char name[]; // null terminated
183 char help[]; // null terminated
184 // perfcounters follow
187 char name[]; // null terminated
188 char help[]; // null terminated
193 struct perfinstance {
195 byte data_offset; // offset of counters from beginning of struct
197 uint category_offset; // offset of category in the shared area
198 char name[]; // null terminated
199 // data follows: this is always 8-byte aligned
205 FTYPE_CATEGORY = 'C',
207 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
208 FTYPE_INSTANCE = 'I',
221 unsigned short num_counters;
222 unsigned short counters_data_size;
224 /* variable length data follows */
230 unsigned int category_offset;
231 /* variable length data follows */
232 char instance_name [1];
238 /* variable length data follows */
247 unsigned int instance_type : 6;
255 unsigned short offset; // offset inside MonoPerfCounters
260 #undef PERFCTR_COUNTER
261 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
262 #define PERFCTR_COUNTER(id,name,help,type,field)
263 static const CategoryDesc
264 predef_categories [] = {
265 #include "mono-perfcounters-def.h"
266 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
270 #undef PERFCTR_COUNTER
271 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
272 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
273 static const CounterDesc
274 predef_counters [] = {
275 #include "mono-perfcounters-def.h"
276 {NULL, NULL, -1, 0, 0}
280 * We have several different classes of counters:
282 * *) runtime counters
284 * *) user-defined counters
285 * *) windows counters (the implementation on windows will use this)
287 * To easily handle the differences we create a vtable for each class that contains the
288 * function pointers with the actual implementation to access the counters.
290 typedef struct _ImplVtable ImplVtable;
292 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
293 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
294 typedef void (*CleanupFunc) (ImplVtable *vtable);
310 MonoPerfCounters *counters;
316 SharedInstance *instance_desc;
317 SharedCounter *counter_desc;
321 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
323 ImplVtable *vtable = g_new0 (ImplVtable, 1);
325 vtable->sample = sample;
326 vtable->update = update;
330 MonoPerfCounters *mono_perfcounters = NULL;
331 static MonoSharedArea *shared_area = NULL;
338 /* maps a pid to a ExternalSArea pointer */
339 static GHashTable *pid_to_shared_area = NULL;
341 static MonoSharedArea *
342 load_sarea_for_pid (int pid)
345 MonoSharedArea *area = NULL;
348 if (pid_to_shared_area == NULL)
349 pid_to_shared_area = g_hash_table_new (NULL, NULL);
350 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
352 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
354 data = g_new (ExternalSArea, 1);
357 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
368 unref_pid_unlocked (int pid)
371 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
374 if (!data->refcount) {
375 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
376 mono_shared_area_unload (data->sarea);
383 predef_cleanup (ImplVtable *vtable)
385 PredefVtable *vt = (PredefVtable*)vtable;
386 /* ExternalSArea *data; */
389 if (!pid_to_shared_area) {
393 unref_pid_unlocked (vt->pid);
398 mono_determine_physical_ram_size (void)
400 #if defined (TARGET_WIN32)
401 MEMORYSTATUSEX memstat;
403 memstat.dwLength = sizeof (memstat);
404 GlobalMemoryStatusEx (&memstat);
405 return (guint64)memstat.ullTotalPhys;
406 #elif defined (__NetBSD__) || defined (__APPLE__)
420 size_t size_sys = sizeof (value);
422 sysctl (mib, 2, &value, &size_sys, NULL, 0);
426 return (guint64)value;
427 #elif defined (HAVE_SYSCONF)
428 guint64 page_size = 0, num_pages = 0;
430 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
431 * reports invalid values, please add your OS specific code below. */
433 page_size = (guint64)sysconf (_SC_PAGESIZE);
436 #ifdef _SC_PHYS_PAGES
437 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
440 if (!page_size || !num_pages) {
441 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
445 return page_size * num_pages;
452 mono_perfcounters_init (void)
454 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
458 InitializeCriticalSection (&perfctr_mutex);
460 shared_area = mono_shared_area ();
461 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
462 shared_area->counters_size = sizeof (MonoPerfCounters);
463 shared_area->data_start = d_offset;
464 shared_area->size = 4096;
465 mono_perfcounters = &shared_area->counters;
469 perfctr_type_compress (int type)
472 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
473 if (simple_type_to_type [i] == type)
476 /* NumberOfItems32 */
480 static unsigned char*
481 shared_data_find_room (int size)
483 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
484 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
489 unsigned short *next;
490 if (*p == FTYPE_END) {
491 if (size < (end - p))
497 next = (unsigned short*)(p + 2);
498 if (*p == FTYPE_DELETED) {
499 /* we reuse only if it's the same size */
509 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
512 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
515 unsigned short *next;
518 next = (unsigned short*)(p + 2);
519 if (!func ((SharedHeader*)p, data))
528 foreach_shared_item (SharedFunc func, void *data)
530 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
531 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
533 foreach_shared_item_in_area (p, end, func, data);
537 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
539 /* FIXME: make this case insensitive */
540 guint16 *strc = mono_string_chars (str);
541 while (*strc == *ascii_str++) {
546 return *strc - *(const unsigned char *)(ascii_str - 1);
555 category_search (SharedHeader *header, void *data)
557 CatSearch *search = data;
558 if (header->ftype == FTYPE_CATEGORY) {
559 SharedCategory *cat = (SharedCategory*)header;
560 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
568 static SharedCategory*
569 find_custom_category (MonoString *name)
574 foreach_shared_item (category_search, &search);
579 category_collect (SharedHeader *header, void *data)
581 GSList **list = data;
582 if (header->ftype == FTYPE_CATEGORY) {
583 *list = g_slist_prepend (*list, header);
589 get_custom_categories (void) {
591 foreach_shared_item (category_collect, &list);
596 custom_category_counters (SharedCategory* cat)
598 char *p = cat->name + strlen (cat->name) + 1;
599 p += strlen (p) + 1; /* skip category help */
603 static SharedCounter*
604 find_custom_counter (SharedCategory* cat, MonoString *name)
607 char *p = custom_category_counters (cat);
608 for (i = 0; i < cat->num_counters; ++i) {
609 SharedCounter *counter = (SharedCounter*)p;
610 if (mono_string_compare_ascii (name, counter->name) == 0)
612 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
613 p += strlen (p) + 1; /* skip counter help */
619 unsigned int cat_offset;
621 MonoString *instance;
622 SharedInstance* result;
627 instance_search (SharedHeader *header, void *data)
629 InstanceSearch *search = data;
630 if (header->ftype == FTYPE_INSTANCE) {
631 SharedInstance *ins = (SharedInstance*)header;
632 if (search->cat_offset == ins->category_offset) {
633 if (search->instance) {
634 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
635 search->result = ins;
639 search->list = g_slist_prepend (search->list, ins);
646 static SharedInstance*
647 find_custom_instance (SharedCategory* cat, MonoString *instance)
649 InstanceSearch search;
650 search.cat_offset = (char*)cat - (char*)shared_area;
652 search.instance = instance;
654 search.result = NULL;
655 foreach_shared_item (instance_search, &search);
656 return search.result;
660 get_custom_instances_list (SharedCategory* cat)
662 InstanceSearch search;
663 search.cat_offset = (char*)cat - (char*)shared_area;
665 search.instance = NULL;
667 search.result = NULL;
668 foreach_shared_item (instance_search, &search);
673 custom_category_help (SharedCategory* cat)
675 return cat->name + strlen (cat->name) + 1;
678 static const CounterDesc*
679 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
681 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
682 const CounterDesc *end = &predef_counters [desc [1].first_counter];
683 for (; cdesc < end; ++cdesc) {
684 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
690 /* fill the info in sample (except the raw value) */
692 fill_sample (MonoCounterSample *sample)
694 sample->timeStamp = mono_100ns_ticks ();
695 sample->timeStamp100nSec = sample->timeStamp;
696 sample->counterTimeStamp = sample->timeStamp;
697 sample->counterFrequency = 10000000;
698 sample->systemFrequency = 10000000;
699 // the real basevalue needs to be get from a different counter...
700 sample->baseValue = 0;
704 id_from_string (MonoString *instance, gboolean is_process)
707 if (mono_string_length (instance)) {
708 char *id_str = mono_string_to_utf8 (instance);
710 id = strtol (id_str, &end, 0);
711 if (end == id_str && !is_process)
719 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
721 MonoProcessError error;
722 int id = GPOINTER_TO_INT (vtable->arg);
726 fill_sample (sample);
727 sample->baseValue = 1;
729 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
731 case COUNTER_CPU_USER_TIME:
732 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
734 case COUNTER_CPU_PRIV_TIME:
735 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
737 case COUNTER_CPU_INTR_TIME:
738 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
740 case COUNTER_CPU_DCP_TIME:
741 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
743 case COUNTER_CPU_PROC_TIME:
744 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
751 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
753 int id = id_from_string (instance, FALSE) << 5;
754 const CounterDesc *cdesc;
756 /* increase the shift above and the mask also in the implementation functions */
757 //g_assert (32 > desc [1].first_counter - desc->first_counter);
758 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
760 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
766 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
768 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
769 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
771 fill_sample (sample);
774 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
776 case COUNTER_NETWORK_BYTESRECSEC:
777 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
779 case COUNTER_NETWORK_BYTESSENTSEC:
780 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
782 case COUNTER_NETWORK_BYTESTOTALSEC:
783 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
787 if (error == MONO_NETWORK_ERROR_NONE)
794 network_cleanup (ImplVtable *vtable)
796 NetworkVtableArg *narg;
812 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
814 const CounterDesc *cdesc;
815 NetworkVtableArg *narg;
820 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
821 instance_name = mono_string_to_utf8 (instance);
822 narg = g_new0 (NetworkVtableArg, 1);
823 narg->id = cdesc->id;
824 narg->name = instance_name;
826 vtable = create_vtable (narg, get_network_counter, NULL);
827 vtable->cleanup = network_cleanup;
834 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
836 int id = GPOINTER_TO_INT (vtable->arg);
842 fill_sample (sample);
843 sample->baseValue = 1;
845 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
847 case COUNTER_PROC_USER_TIME:
848 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
850 case COUNTER_PROC_PRIV_TIME:
851 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
853 case COUNTER_PROC_PROC_TIME:
854 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
856 case COUNTER_PROC_THREADS:
857 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
859 case COUNTER_PROC_VBYTES:
860 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
862 case COUNTER_PROC_WSET:
863 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
865 case COUNTER_PROC_PBYTES:
866 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
873 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
875 int id = id_from_string (instance, TRUE) << 5;
876 const CounterDesc *cdesc;
878 /* increase the shift above and the mask also in the implementation functions */
879 //g_assert (32 > desc [1].first_counter - desc->first_counter);
880 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
882 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
888 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
890 int id = GPOINTER_TO_INT (vtable->arg);
892 fill_sample (sample);
893 sample->baseValue = 1;
895 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
897 case COUNTER_MEM_NUM_OBJECTS:
898 sample->rawValue = mono_stats.new_object_count;
900 case COUNTER_MEM_PHYS_TOTAL:
901 sample->rawValue = mono_determine_physical_ram_size ();;
908 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
910 const CounterDesc *cdesc;
912 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
914 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
920 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
922 PredefVtable *vt = (PredefVtable *)vtable;
923 const CounterDesc *desc;
924 int cat_id = GPOINTER_TO_INT (vtable->arg);
925 int id = cat_id >> 16;
928 fill_sample (sample);
929 sample->baseValue = 1;
931 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
932 sample->counterType = desc->type;
933 /* FIXME: check that the offset fits inside imported counters */
934 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
935 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
940 predef_vtable (void *arg, MonoString *instance)
942 MonoSharedArea *area;
943 PredefVtable *vtable;
944 char *pids = mono_string_to_utf8 (instance);
949 area = load_sarea_for_pid (pid);
953 vtable = g_new (PredefVtable, 1);
954 vtable->vtable.arg = arg;
955 vtable->vtable.sample = predef_readonly_counter;
956 vtable->vtable.cleanup = predef_cleanup;
957 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
960 return (ImplVtable*)vtable;
963 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
964 * this needs some way to set sample->counterType as well, though.
967 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
969 int cat_id = GPOINTER_TO_INT (vtable->arg);
970 int id = cat_id >> 16;
973 fill_sample (sample);
974 sample->baseValue = 1;
976 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
980 case COUNTER_EXC_THROWN:
981 sample->rawValue = mono_perfcounters->exceptions_thrown;
985 case CATEGORY_ASPNET:
987 case COUNTER_ASPNET_REQ_Q:
988 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
990 case COUNTER_ASPNET_REQ_TOTAL:
991 sample->rawValue = mono_perfcounters->aspnet_requests;
995 case CATEGORY_THREADPOOL:
997 case COUNTER_THREADPOOL_WORKITEMS:
998 sample->rawValue = mono_perfcounters->threadpool_workitems;
1000 case COUNTER_THREADPOOL_IOWORKITEMS:
1001 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1003 case COUNTER_THREADPOOL_THREADS:
1004 sample->rawValue = mono_perfcounters->threadpool_threads;
1006 case COUNTER_THREADPOOL_IOTHREADS:
1007 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1013 case COUNTER_JIT_BYTES:
1014 sample->rawValue = mono_perfcounters->jit_bytes;
1016 case COUNTER_JIT_METHODS:
1017 sample->rawValue = mono_perfcounters->jit_methods;
1019 case COUNTER_JIT_TIME:
1020 sample->rawValue = mono_perfcounters->jit_time;
1022 case COUNTER_JIT_BYTES_PSEC:
1023 sample->rawValue = mono_perfcounters->jit_bytes;
1025 case COUNTER_JIT_FAILURES:
1026 sample->rawValue = mono_perfcounters->jit_failures;
1035 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1037 guint32 *volatile ptr = NULL;
1038 gint64 *volatile ptr64 = NULL;
1039 int cat_id = GPOINTER_TO_INT (vtable->arg);
1040 int id = cat_id >> 16;
1043 case CATEGORY_ASPNET:
1045 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1046 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1049 case CATEGORY_THREADPOOL:
1051 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1052 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1053 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1054 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1061 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1063 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1068 /* this can be non-atomic */
1073 /* FIXME: we need to do this atomically */
1074 /* No InterlockedIncrement64() yet */
1077 return InterlockedIncrement64 (ptr);
1079 return InterlockedDecrement64 (ptr);
1085 /* this can be non-atomic */
1093 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1095 const CounterDesc *cdesc;
1097 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1098 *type = cdesc->type;
1099 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1100 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1102 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1108 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1110 CustomVTable *counter_data = (CustomVTable *)vtable;
1112 fill_sample (sample);
1113 sample->baseValue = 1;
1115 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1117 sample->rawValue = 0;
1119 sample->rawValue = *(guint64*)vtable->arg;
1124 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1126 /* FIXME: check writability */
1127 guint64 *ptr = vtable->arg;
1130 /* FIXME: we need to do this atomically */
1134 /* this can be non-atomic */
1141 static SharedInstance*
1142 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
1144 SharedInstance* inst;
1147 int size, data_offset;
1149 inst = find_custom_instance (cat, instance);
1152 name = mono_string_to_utf8 (instance);
1153 size = sizeof (SharedInstance) + strlen (name);
1157 size += (sizeof (guint64) * cat->num_counters);
1159 ptr = shared_data_find_room (size);
1165 inst = (SharedInstance*)ptr;
1166 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
1167 inst->header.size = size;
1168 inst->category_offset = (char*)cat - (char*)shared_area;
1169 cat->num_instances++;
1170 /* now copy the variable data */
1171 p = inst->instance_name;
1173 p += strlen (name) + 1;
1174 inst->header.ftype = FTYPE_INSTANCE;
1182 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1184 CustomVTable* vtable;
1185 vtable = g_new0 (CustomVTable, 1);
1186 vtable->vtable.arg = data;
1187 vtable->vtable.sample = custom_writable_counter;
1188 vtable->vtable.update = custom_writable_update;
1189 vtable->instance_desc = inst;
1190 vtable->counter_desc = scounter;
1192 return (ImplVtable*)vtable;
1196 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1198 SharedCounter *scounter;
1199 SharedInstance* inst;
1202 scounter = find_custom_counter (cat, counter);
1205 *type = simple_type_to_type [scounter->type];
1206 inst = custom_get_instance (cat, scounter, instance);
1209 size = sizeof (SharedInstance) + strlen (inst->instance_name);
1212 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1215 static const CategoryDesc*
1216 find_category (MonoString *category)
1219 for (i = 0; i < NUM_CATEGORIES; ++i) {
1220 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1221 return &predef_categories [i];
1227 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1228 MonoString* machine, int *type, MonoBoolean *custom)
1230 const CategoryDesc *cdesc;
1231 /* no support for counters on other machines */
1232 if (mono_string_compare_ascii (machine, "."))
1234 cdesc = find_category (category);
1236 SharedCategory *scat = find_custom_category (category);
1240 return custom_get_impl (scat, counter, instance, type);
1242 switch (cdesc->id) {
1244 return cpu_get_impl (counter, instance, type, custom);
1246 return process_get_impl (counter, instance, type, custom);
1247 case CATEGORY_MONO_MEM:
1248 return mono_mem_get_impl (counter, instance, type, custom);
1249 case CATEGORY_NETWORK:
1250 return network_get_impl (counter, instance, type, custom);
1254 case CATEGORY_REMOTING:
1255 case CATEGORY_LOADING:
1256 case CATEGORY_THREAD:
1257 case CATEGORY_INTEROP:
1258 case CATEGORY_SECURITY:
1259 case CATEGORY_ASPNET:
1260 case CATEGORY_THREADPOOL:
1261 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1267 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1269 ImplVtable *vtable = impl;
1270 if (vtable && vtable->sample)
1271 return vtable->sample (vtable, only_value, sample);
1276 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1278 ImplVtable *vtable = impl;
1279 if (vtable && vtable->update)
1280 return vtable->update (vtable, do_incr, value);
1285 mono_perfcounter_free_data (void *impl)
1287 ImplVtable *vtable = impl;
1288 if (vtable && vtable->cleanup)
1289 vtable->cleanup (vtable);
1293 /* Category icalls */
1295 mono_perfcounter_category_del (MonoString *name)
1297 const CategoryDesc *cdesc;
1298 SharedCategory *cat;
1299 cdesc = find_category (name);
1300 /* can't delete a predefined category */
1304 cat = find_custom_category (name);
1305 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1306 if (!cat || cat->num_instances) {
1310 cat->header.ftype = FTYPE_DELETED;
1316 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1318 const CategoryDesc *cdesc;
1319 /* no support for counters on other machines */
1320 if (mono_string_compare_ascii (machine, "."))
1322 cdesc = find_category (category);
1324 SharedCategory *scat = find_custom_category (category);
1327 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1329 return mono_string_new (mono_domain_get (), cdesc->help);
1333 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1334 * TRUE only if a counter with that name exists in the category.
1337 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1339 const CategoryDesc *cdesc;
1340 /* no support for counters on other machines */
1341 if (mono_string_compare_ascii (machine, "."))
1343 cdesc = find_category (category);
1345 SharedCategory *scat = find_custom_category (category);
1348 /* counter is allowed to be null */
1351 /* search through the custom category */
1352 return find_custom_counter (scat, counter) != NULL;
1354 /* counter is allowed to be null */
1357 if (get_counter_in_category (cdesc, counter))
1362 /* C map of the type with the same name */
1368 } CounterCreationData;
1371 * Since we'll keep a copy of the category per-process, we should also make sure
1372 * categories with the same name are compatible.
1375 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1380 int num_counters = mono_array_length (items);
1381 int counters_data_size;
1384 char **counter_info = NULL;
1387 SharedCategory *cat;
1389 /* FIXME: ensure there isn't a category created already */
1390 mono_error_init (&error);
1391 name = mono_string_to_utf8_checked (category, &error);
1392 if (!mono_error_ok (&error))
1394 chelp = mono_string_to_utf8_checked (help, &error);
1395 if (!mono_error_ok (&error))
1397 counter_info = g_new0 (char*, num_counters * 2);
1398 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1399 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1400 for (i = 0; i < num_counters; ++i) {
1401 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1402 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1403 if (!mono_error_ok (&error))
1405 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1406 if (!mono_error_ok (&error))
1408 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1410 for (i = 0; i < num_counters * 2; ++i) {
1411 if (!counter_info [i])
1413 size += strlen (counter_info [i]) + 1;
1417 counters_data_size = num_counters * 8; /* optimize for size later */
1421 ptr = shared_data_find_room (size);
1426 cat = (SharedCategory*)ptr;
1427 cat->header.extra = type;
1428 cat->header.size = size;
1429 cat->num_counters = num_counters;
1430 cat->counters_data_size = counters_data_size;
1431 /* now copy the vaiable data */
1434 p += strlen (name) + 1;
1436 p += strlen (chelp) + 1;
1437 for (i = 0; i < num_counters; ++i) {
1438 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1439 /* emit the SharedCounter structures */
1440 *p++ = perfctr_type_compress (data->type);
1442 strcpy (p, counter_info [i * 2]);
1443 p += strlen (counter_info [i * 2]) + 1;
1444 strcpy (p, counter_info [i * 2 + 1]);
1445 p += strlen (counter_info [i * 2 + 1]) + 1;
1447 cat->header.ftype = FTYPE_CATEGORY;
1453 for (i = 0; i < num_counters * 2; ++i) {
1454 g_free (counter_info [i]);
1456 g_free (counter_info);
1460 mono_error_cleanup (&error);
1465 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1467 const CategoryDesc *cdesc;
1468 /* no support for counters on other machines */
1469 /*FIXME: machine appears to be wrong
1470 if (mono_string_compare_ascii (machine, "."))
1472 cdesc = find_category (category);
1474 SharedCategory *scat;
1475 scat = find_custom_category (category);
1478 if (find_custom_instance (scat, instance))
1481 /* FIXME: search instance */
1487 mono_perfcounter_category_names (MonoString *machine)
1491 MonoDomain *domain = mono_domain_get ();
1492 GSList *custom_categories, *tmp;
1493 /* no support for counters on other machines */
1494 if (mono_string_compare_ascii (machine, "."))
1495 return mono_array_new (domain, mono_get_string_class (), 0);
1497 custom_categories = get_custom_categories ();
1498 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1499 for (i = 0; i < NUM_CATEGORIES; ++i) {
1500 const CategoryDesc *cdesc = &predef_categories [i];
1501 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1503 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1504 SharedCategory *scat = tmp->data;
1505 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1509 g_slist_free (custom_categories);
1514 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1517 SharedCategory *scat;
1518 const CategoryDesc *cdesc;
1520 MonoDomain *domain = mono_domain_get ();
1521 /* no support for counters on other machines */
1522 if (mono_string_compare_ascii (machine, "."))
1523 return mono_array_new (domain, mono_get_string_class (), 0);
1524 cdesc = find_category (category);
1526 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1527 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1528 const CounterDesc *desc = &predef_counters [i];
1529 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1534 scat = find_custom_category (category);
1536 char *p = custom_category_counters (scat);
1538 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1539 for (i = 0; i < scat->num_counters; ++i) {
1540 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1541 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1542 p += strlen (p) + 1; /* skip counter help */
1548 return mono_array_new (domain, mono_get_string_class (), 0);
1552 get_string_array (void **array, int count, gboolean is_process)
1555 MonoDomain *domain = mono_domain_get ();
1556 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1557 for (i = 0; i < count; ++i) {
1561 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1562 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1564 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1567 mono_array_setref (res, i, mono_string_new (domain, p));
1575 get_string_array_of_strings (void **array, int count)
1578 MonoDomain *domain = mono_domain_get ();
1579 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1580 for (i = 0; i < count; ++i) {
1582 mono_array_setref (res, i, mono_string_new (domain, p));
1589 get_mono_instances (void)
1598 buf = g_new (void*, count);
1599 res = mono_shared_area_instances (buf, count);
1600 } while (res == count);
1601 array = get_string_array (buf, res, TRUE);
1607 get_cpu_instances (void)
1613 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1614 buf = g_new (void*, count);
1615 for (i = 0; i < count; ++i)
1616 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1617 array = get_string_array (buf, count, FALSE);
1619 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1624 get_processes_instances (void)
1628 void **buf = mono_process_list (&count);
1630 return get_string_array (NULL, 0, FALSE);
1631 array = get_string_array (buf, count, TRUE);
1637 get_networkinterface_instances (void)
1641 void **buf = mono_networkinterface_list (&count);
1643 return get_string_array_of_strings (NULL, 0);
1644 array = get_string_array_of_strings (buf, count);
1645 g_strfreev ((char **) buf);
1650 get_custom_instances (MonoString *category)
1652 SharedCategory *scat;
1653 scat = find_custom_category (category);
1655 GSList *list = get_custom_instances_list (scat);
1658 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1659 for (tmp = list; tmp; tmp = tmp->next) {
1660 SharedInstance *inst = tmp->data;
1661 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1664 g_slist_free (list);
1667 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1671 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1673 const CategoryDesc* cat;
1674 if (mono_string_compare_ascii (machine, "."))
1675 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1676 cat = find_category (category);
1678 return get_custom_instances (category);
1679 switch (cat->instance_type) {
1681 return get_mono_instances ();
1683 return get_cpu_instances ();
1684 case ProcessInstance:
1685 return get_processes_instances ();
1686 case NetworkInterfaceInstance:
1687 return get_networkinterface_instances ();
1688 case ThreadInstance:
1690 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1695 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1697 g_assert_not_reached ();
1701 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1703 g_assert_not_reached ();
1707 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1709 g_assert_not_reached ();
1713 mono_perfcounter_free_data (void *impl)
1715 g_assert_not_reached ();
1718 /* Category icalls */
1720 mono_perfcounter_category_del (MonoString *name)
1722 g_assert_not_reached ();
1726 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1728 g_assert_not_reached ();
1732 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1734 g_assert_not_reached ();
1738 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1740 g_assert_not_reached ();
1744 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1746 g_assert_not_reached ();
1750 mono_perfcounter_category_names (MonoString *machine)
1752 g_assert_not_reached ();
1756 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1758 g_assert_not_reached ();
1762 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1764 g_assert_not_reached ();