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 (__APPLE__)
29 #include <mach/message.h>
30 #include <mach/mach_host.h>
31 #include <mach/host_info.h>
33 #if defined (__NetBSD__) || defined (__APPLE__)
34 #include <sys/sysctl.h>
36 #include "metadata/mono-perfcounters.h"
37 #include "metadata/appdomain.h"
38 #include "metadata/object-internals.h"
40 #include "metadata/class-internals.h"
41 #include "utils/mono-time.h"
42 #include "utils/mono-mmap.h"
43 #include "utils/mono-proclib.h"
44 #include "utils/mono-networkinterfaces.h"
45 #include "utils/mono-error-internals.h"
46 #include "utils/atomic.h"
47 #include <mono/io-layer/io-layer.h>
49 /* map of CounterSample.cs */
50 struct _MonoCounterSample {
53 gint64 counterFrequency;
54 gint64 systemFrequency;
56 gint64 timeStamp100nSec;
57 gint64 counterTimeStamp;
61 #ifndef DISABLE_PERFCOUNTERS
62 /* map of PerformanceCounterType.cs */
64 NumberOfItemsHEX32=0x00000000,
65 NumberOfItemsHEX64=0x00000100,
66 NumberOfItems32=0x00010000,
67 NumberOfItems64=0x00010100,
68 CounterDelta32=0x00400400,
69 CounterDelta64=0x00400500,
70 SampleCounter=0x00410400,
71 CountPerTimeInterval32=0x00450400,
72 CountPerTimeInterval64=0x00450500,
73 RateOfCountsPerSecond32=0x10410400,
74 RateOfCountsPerSecond64=0x10410500,
75 RawFraction=0x20020400,
76 CounterTimer=0x20410500,
77 Timer100Ns=0x20510500,
78 SampleFraction=0x20C20400,
79 CounterTimerInverse=0x21410500,
80 Timer100NsInverse=0x21510500,
81 CounterMultiTimer=0x22410500,
82 CounterMultiTimer100Ns=0x22510500,
83 CounterMultiTimerInverse=0x23410500,
84 CounterMultiTimer100NsInverse=0x23510500,
85 AverageTimer32=0x30020400,
86 ElapsedTime=0x30240500,
87 AverageCount64=0x40020500,
88 SampleBase=0x40030401,
89 AverageBase=0x40030402,
91 CounterMultiBase=0x42030500
94 /* maps a small integer type to the counter types above */
96 simple_type_to_type [] = {
97 NumberOfItemsHEX32, NumberOfItemsHEX64,
98 NumberOfItems32, NumberOfItems64,
99 CounterDelta32, CounterDelta64,
100 SampleCounter, CountPerTimeInterval32,
101 CountPerTimeInterval64, RateOfCountsPerSecond32,
102 RateOfCountsPerSecond64, RawFraction,
103 CounterTimer, Timer100Ns,
104 SampleFraction, CounterTimerInverse,
105 Timer100NsInverse, CounterMultiTimer,
106 CounterMultiTimer100Ns, CounterMultiTimerInverse,
107 CounterMultiTimer100NsInverse, AverageTimer32,
108 ElapsedTime, AverageCount64,
109 SampleBase, AverageBase,
110 RawBase, CounterMultiBase
124 NetworkInterfaceInstance,
128 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
129 #define PERFCTR_COUNTER(id,name,help,type,field)
131 #include "mono-perfcounters-def.h"
136 #undef PERFCTR_COUNTER
137 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
138 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
139 /* each counter is assigned an id starting from 0 inside the category */
141 #include "mono-perfcounters-def.h"
146 #undef PERFCTR_COUNTER
147 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
148 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
149 /* this is used just to count the number of counters */
151 #include "mono-perfcounters-def.h"
155 static mono_mutex_t perfctr_mutex;
156 #define perfctr_lock() mono_os_mutex_lock (&perfctr_mutex)
157 #define perfctr_unlock() mono_os_mutex_unlock (&perfctr_mutex)
162 unsigned short counters_start;
163 unsigned short counters_size;
164 unsigned short data_start;
165 MonoPerfCounters counters;
170 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
172 struct stanza_header {
173 byte stanza_type; // FTYPE_*
175 ushort stanza_length; // includeas header
180 // perfcat and perfinstance are 4-bytes aligned
184 ushort length; // includes the counters
186 ushort counters_data_size;
188 char name[]; // null terminated
189 char help[]; // null terminated
190 // perfcounters follow
193 char name[]; // null terminated
194 char help[]; // null terminated
199 struct perfinstance {
201 byte data_offset; // offset of counters from beginning of struct
203 uint category_offset; // offset of category in the shared area
204 char name[]; // null terminated
205 // data follows: this is always 8-byte aligned
211 FTYPE_CATEGORY = 'C',
213 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
214 FTYPE_INSTANCE = 'I',
227 unsigned short num_counters;
228 unsigned short counters_data_size;
230 /* variable length data follows */
234 // SharedCounter counters_info [num_counters]
239 unsigned int category_offset;
240 /* variable length data follows */
241 char instance_name [1];
248 /* variable length data follows */
259 unsigned int instance_type : 6;
267 unsigned short offset; // offset inside MonoPerfCounters
272 #undef PERFCTR_COUNTER
273 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
274 #define PERFCTR_COUNTER(id,name,help,type,field)
275 static const CategoryDesc
276 predef_categories [] = {
277 #include "mono-perfcounters-def.h"
278 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
282 #undef PERFCTR_COUNTER
283 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
284 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
285 static const CounterDesc
286 predef_counters [] = {
287 #include "mono-perfcounters-def.h"
288 {NULL, NULL, -1, 0, 0}
292 * We have several different classes of counters:
294 * *) runtime counters
296 * *) user-defined counters
297 * *) windows counters (the implementation on windows will use this)
299 * To easily handle the differences we create a vtable for each class that contains the
300 * function pointers with the actual implementation to access the counters.
302 typedef struct _ImplVtable ImplVtable;
304 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
305 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
306 typedef void (*CleanupFunc) (ImplVtable *vtable);
322 MonoPerfCounters *counters;
328 SharedInstance *instance_desc;
329 SharedCounter *counter_desc;
333 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
335 ImplVtable *vtable = g_new0 (ImplVtable, 1);
337 vtable->sample = sample;
338 vtable->update = update;
342 MonoPerfCounters *mono_perfcounters = NULL;
343 static MonoSharedArea *shared_area = NULL;
350 /* maps a pid to a ExternalSArea pointer */
351 static GHashTable *pid_to_shared_area = NULL;
353 static MonoSharedArea *
354 load_sarea_for_pid (int pid)
357 MonoSharedArea *area = NULL;
360 if (pid_to_shared_area == NULL)
361 pid_to_shared_area = g_hash_table_new (NULL, NULL);
362 data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
364 area = (MonoSharedArea *)mono_shared_area_for_pid (GINT_TO_POINTER (pid));
366 data = g_new (ExternalSArea, 1);
369 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
372 area = (MonoSharedArea *)data->sarea;
380 unref_pid_unlocked (int pid)
383 data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
386 if (!data->refcount) {
387 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
388 mono_shared_area_unload (data->sarea);
395 predef_cleanup (ImplVtable *vtable)
397 PredefVtable *vt = (PredefVtable*)vtable;
398 /* ExternalSArea *data; */
401 if (!pid_to_shared_area) {
405 unref_pid_unlocked (vt->pid);
410 mono_determine_physical_ram_size (void)
412 #if defined (TARGET_WIN32)
413 MEMORYSTATUSEX memstat;
415 memstat.dwLength = sizeof (memstat);
416 GlobalMemoryStatusEx (&memstat);
417 return (guint64)memstat.ullTotalPhys;
418 #elif defined (__NetBSD__) || defined (__APPLE__)
432 size_t size_sys = sizeof (value);
434 sysctl (mib, 2, &value, &size_sys, NULL, 0);
438 return (guint64)value;
439 #elif defined (HAVE_SYSCONF)
440 guint64 page_size = 0, num_pages = 0;
442 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
443 * reports invalid values, please add your OS specific code below. */
445 page_size = (guint64)sysconf (_SC_PAGESIZE);
448 #ifdef _SC_PHYS_PAGES
449 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
452 if (!page_size || !num_pages) {
453 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
457 return page_size * num_pages;
464 mono_determine_physical_ram_available_size (void)
466 #if defined (TARGET_WIN32)
467 MEMORYSTATUSEX memstat;
469 memstat.dwLength = sizeof (memstat);
470 GlobalMemoryStatusEx (&memstat);
471 return (guint64)memstat.ullAvailPhys;
473 #elif defined (__NetBSD__)
474 struct vmtotal vm_total;
482 #if defined (VM_METER)
488 len = sizeof (vm_total);
489 sysctl (mib, 2, &vm_total, &len, NULL, 0);
495 len = sizeof (page_size);
496 sysctl (mib, 2, &page_size, &len, NULL, 0
498 return ((guint64) value.t_free * page_size) / 1024;
499 #elif defined (__APPLE__)
500 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
501 mach_port_t host = mach_host_self();
503 vm_statistics_data_t vmstat;
504 if (KERN_SUCCESS != host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &count)) {
505 g_warning ("Mono was unable to retrieve memory usage!");
509 host_page_size(host, &page_size);
510 return (guint64) vmstat.free_count * page_size;
512 #elif defined (HAVE_SYSCONF)
513 guint64 page_size = 0, num_pages = 0;
515 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
516 * reports invalid values, please add your OS specific code below. */
518 page_size = (guint64)sysconf (_SC_PAGESIZE);
521 #ifdef _SC_AVPHYS_PAGES
522 num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
525 if (!page_size || !num_pages) {
526 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
530 return page_size * num_pages;
537 mono_perfcounters_init (void)
539 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
543 mono_os_mutex_init_recursive (&perfctr_mutex);
545 shared_area = (MonoSharedArea *)mono_shared_area ();
546 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
547 shared_area->counters_size = sizeof (MonoPerfCounters);
548 shared_area->data_start = d_offset;
549 shared_area->size = 4096;
550 mono_perfcounters = &shared_area->counters;
554 perfctr_type_compress (int type)
557 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
558 if (simple_type_to_type [i] == type)
561 /* NumberOfItems32 */
566 shared_data_reserve_room (int size, int ftype)
568 SharedHeader* header;
569 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
570 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
575 unsigned short *next;
576 if (*p == FTYPE_END) {
577 if (size < (end - p))
583 next = (unsigned short*)(p + 2);
584 if (*p == FTYPE_DELETED) {
585 /* we reuse only if it's the same size */
595 header = (SharedHeader*)p;
596 header->ftype = ftype;
597 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
603 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
606 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
609 unsigned short *next;
612 next = (unsigned short*)(p + 2);
613 if (!func ((SharedHeader*)p, data))
622 foreach_shared_item (SharedFunc func, void *data)
624 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
625 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
627 foreach_shared_item_in_area (p, end, func, data);
631 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
633 /* FIXME: make this case insensitive */
634 guint16 *strc = mono_string_chars (str);
635 while (*strc == *ascii_str++) {
640 return *strc - *(const unsigned char *)(ascii_str - 1);
649 category_search (SharedHeader *header, void *data)
651 CatSearch *search = (CatSearch *)data;
652 if (header->ftype == FTYPE_CATEGORY) {
653 SharedCategory *cat = (SharedCategory*)header;
654 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
662 static SharedCategory*
663 find_custom_category (MonoString *name)
668 foreach_shared_item (category_search, &search);
673 category_collect (SharedHeader *header, void *data)
675 GSList **list = (GSList **)data;
676 if (header->ftype == FTYPE_CATEGORY) {
677 *list = g_slist_prepend (*list, header);
683 get_custom_categories (void) {
685 foreach_shared_item (category_collect, &list);
690 custom_category_counters (SharedCategory* cat)
692 char *p = cat->name + strlen (cat->name) + 1;
693 p += strlen (p) + 1; /* skip category help */
697 static SharedCounter*
698 find_custom_counter (SharedCategory* cat, MonoString *name)
701 char *p = custom_category_counters (cat);
702 for (i = 0; i < cat->num_counters; ++i) {
703 SharedCounter *counter = (SharedCounter*)p;
704 if (mono_string_compare_ascii (name, counter->name) == 0)
706 p += 2; /* skip counter type */
707 p += strlen (p) + 1; /* skip counter name */
708 p += strlen (p) + 1; /* skip counter help */
714 unsigned int cat_offset;
717 SharedInstance* result;
722 instance_search (SharedHeader *header, void *data)
724 InstanceSearch *search = (InstanceSearch *)data;
725 if (header->ftype == FTYPE_INSTANCE) {
726 SharedInstance *ins = (SharedInstance*)header;
727 if (search->cat_offset == ins->category_offset) {
729 if (strcmp (search->name, ins->instance_name) == 0) {
730 search->result = ins;
734 search->list = g_slist_prepend (search->list, ins);
741 static SharedInstance*
742 find_custom_instance (SharedCategory* cat, char *name)
744 InstanceSearch search;
745 search.cat_offset = (char*)cat - (char*)shared_area;
749 search.result = NULL;
750 foreach_shared_item (instance_search, &search);
751 return search.result;
755 get_custom_instances_list (SharedCategory* cat)
757 InstanceSearch search;
758 search.cat_offset = (char*)cat - (char*)shared_area;
762 search.result = NULL;
763 foreach_shared_item (instance_search, &search);
768 custom_category_help (SharedCategory* cat)
770 return cat->name + strlen (cat->name) + 1;
773 static const CounterDesc*
774 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
776 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
777 const CounterDesc *end = &predef_counters [desc [1].first_counter];
778 for (; cdesc < end; ++cdesc) {
779 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
785 /* fill the info in sample (except the raw value) */
787 fill_sample (MonoCounterSample *sample)
789 sample->timeStamp = mono_100ns_ticks ();
790 sample->timeStamp100nSec = sample->timeStamp;
791 sample->counterTimeStamp = sample->timeStamp;
792 sample->counterFrequency = 10000000;
793 sample->systemFrequency = 10000000;
794 // the real basevalue needs to be get from a different counter...
795 sample->baseValue = 0;
799 id_from_string (MonoString *instance, gboolean is_process)
802 if (mono_string_length (instance)) {
803 char *id_str = mono_string_to_utf8 (instance);
805 id = strtol (id_str, &end, 0);
806 if (end == id_str && !is_process)
814 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
816 MonoProcessError error;
817 int id = GPOINTER_TO_INT (vtable->arg);
821 fill_sample (sample);
822 sample->baseValue = 1;
824 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
826 case COUNTER_CPU_USER_TIME:
827 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
829 case COUNTER_CPU_PRIV_TIME:
830 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
832 case COUNTER_CPU_INTR_TIME:
833 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
835 case COUNTER_CPU_DCP_TIME:
836 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
838 case COUNTER_CPU_PROC_TIME:
839 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
846 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
848 int id = id_from_string (instance, FALSE) << 5;
849 const CounterDesc *cdesc;
851 /* increase the shift above and the mask also in the implementation functions */
852 //g_assert (32 > desc [1].first_counter - desc->first_counter);
853 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
855 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
861 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
863 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
864 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
866 fill_sample (sample);
869 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
871 case COUNTER_NETWORK_BYTESRECSEC:
872 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
874 case COUNTER_NETWORK_BYTESSENTSEC:
875 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
877 case COUNTER_NETWORK_BYTESTOTALSEC:
878 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
882 if (error == MONO_NETWORK_ERROR_NONE)
889 network_cleanup (ImplVtable *vtable)
891 NetworkVtableArg *narg;
896 narg = (NetworkVtableArg *)vtable->arg;
907 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
909 const CounterDesc *cdesc;
910 NetworkVtableArg *narg;
915 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
916 instance_name = mono_string_to_utf8 (instance);
917 narg = g_new0 (NetworkVtableArg, 1);
918 narg->id = cdesc->id;
919 narg->name = instance_name;
921 vtable = create_vtable (narg, get_network_counter, NULL);
922 vtable->cleanup = network_cleanup;
929 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
931 int id = GPOINTER_TO_INT (vtable->arg);
937 fill_sample (sample);
938 sample->baseValue = 1;
940 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
942 case COUNTER_PROC_USER_TIME:
943 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
945 case COUNTER_PROC_PRIV_TIME:
946 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
948 case COUNTER_PROC_PROC_TIME:
949 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
951 case COUNTER_PROC_THREADS:
952 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
954 case COUNTER_PROC_VBYTES:
955 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
957 case COUNTER_PROC_WSET:
958 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
960 case COUNTER_PROC_PBYTES:
961 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
968 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
970 int id = id_from_string (instance, TRUE) << 5;
971 const CounterDesc *cdesc;
973 /* increase the shift above and the mask also in the implementation functions */
974 //g_assert (32 > desc [1].first_counter - desc->first_counter);
975 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
977 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
983 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
985 int id = GPOINTER_TO_INT (vtable->arg);
987 fill_sample (sample);
988 sample->baseValue = 1;
990 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
992 case COUNTER_MEM_NUM_OBJECTS:
993 sample->rawValue = 0;
995 case COUNTER_MEM_PHYS_TOTAL:
996 sample->rawValue = mono_determine_physical_ram_size ();;
998 case COUNTER_MEM_PHYS_AVAILABLE:
999 sample->rawValue = mono_determine_physical_ram_available_size ();;
1006 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1008 const CounterDesc *cdesc;
1010 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1011 *type = cdesc->type;
1012 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1018 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1020 PredefVtable *vt = (PredefVtable *)vtable;
1021 const CounterDesc *desc;
1022 int cat_id = GPOINTER_TO_INT (vtable->arg);
1023 int id = cat_id >> 16;
1026 fill_sample (sample);
1027 sample->baseValue = 1;
1029 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1030 sample->counterType = desc->type;
1031 /* FIXME: check that the offset fits inside imported counters */
1032 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1033 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1038 predef_vtable (void *arg, MonoString *instance)
1040 MonoSharedArea *area;
1041 PredefVtable *vtable;
1042 char *pids = mono_string_to_utf8 (instance);
1047 area = load_sarea_for_pid (pid);
1051 vtable = g_new (PredefVtable, 1);
1052 vtable->vtable.arg = arg;
1053 vtable->vtable.sample = predef_readonly_counter;
1054 vtable->vtable.cleanup = predef_cleanup;
1055 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1058 return (ImplVtable*)vtable;
1061 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1062 * this needs some way to set sample->counterType as well, though.
1065 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1067 int cat_id = GPOINTER_TO_INT (vtable->arg);
1068 int id = cat_id >> 16;
1071 fill_sample (sample);
1072 sample->baseValue = 1;
1074 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1078 case COUNTER_EXC_THROWN:
1079 sample->rawValue = mono_perfcounters->exceptions_thrown;
1083 case CATEGORY_ASPNET:
1085 case COUNTER_ASPNET_REQ_Q:
1086 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1088 case COUNTER_ASPNET_REQ_TOTAL:
1089 sample->rawValue = mono_perfcounters->aspnet_requests;
1093 case CATEGORY_THREADPOOL:
1095 case COUNTER_THREADPOOL_WORKITEMS:
1096 sample->rawValue = mono_perfcounters->threadpool_workitems;
1098 case COUNTER_THREADPOOL_IOWORKITEMS:
1099 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1101 case COUNTER_THREADPOOL_THREADS:
1102 sample->rawValue = mono_perfcounters->threadpool_threads;
1104 case COUNTER_THREADPOOL_IOTHREADS:
1105 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1111 case COUNTER_JIT_BYTES:
1112 sample->rawValue = mono_perfcounters->jit_bytes;
1114 case COUNTER_JIT_METHODS:
1115 sample->rawValue = mono_perfcounters->jit_methods;
1117 case COUNTER_JIT_TIME:
1118 sample->rawValue = mono_perfcounters->jit_time;
1120 case COUNTER_JIT_BYTES_PSEC:
1121 sample->rawValue = mono_perfcounters->jit_bytes;
1123 case COUNTER_JIT_FAILURES:
1124 sample->rawValue = mono_perfcounters->jit_failures;
1133 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1135 guint32 *volatile ptr = NULL;
1136 gint64 *volatile ptr64 = NULL;
1137 int cat_id = GPOINTER_TO_INT (vtable->arg);
1138 int id = cat_id >> 16;
1141 case CATEGORY_ASPNET:
1143 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1144 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1147 case CATEGORY_THREADPOOL:
1149 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1150 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1151 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1152 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1159 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1161 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1166 /* this can be non-atomic */
1171 /* FIXME: we need to do this atomically */
1172 /* No InterlockedIncrement64() yet */
1175 return InterlockedIncrement64 (ptr);
1177 return InterlockedDecrement64 (ptr);
1183 /* this can be non-atomic */
1191 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1193 const CounterDesc *cdesc;
1195 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1196 *type = cdesc->type;
1197 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1198 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1200 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1206 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1208 CustomVTable *counter_data = (CustomVTable *)vtable;
1210 fill_sample (sample);
1211 sample->baseValue = 1;
1213 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1215 sample->rawValue = 0;
1217 sample->rawValue = *(guint64*)vtable->arg;
1222 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1224 /* FIXME: check writability */
1225 guint64 *ptr = (guint64 *)vtable->arg;
1228 /* FIXME: we need to do this atomically */
1232 /* this can be non-atomic */
1239 static SharedInstance*
1240 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1242 SharedInstance* inst;
1245 inst = find_custom_instance (cat, name);
1248 size = sizeof (SharedInstance) + strlen (name);
1251 size += (sizeof (guint64) * cat->num_counters);
1253 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1259 inst->category_offset = (char*)cat - (char*)shared_area;
1260 cat->num_instances++;
1261 /* now copy the variable data */
1262 p = inst->instance_name;
1264 p += strlen (name) + 1;
1271 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1273 CustomVTable* vtable;
1274 vtable = g_new0 (CustomVTable, 1);
1275 vtable->vtable.arg = data;
1276 vtable->vtable.sample = custom_writable_counter;
1277 vtable->vtable.update = custom_writable_update;
1278 vtable->instance_desc = inst;
1279 vtable->counter_desc = scounter;
1281 return (ImplVtable*)vtable;
1285 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1287 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1290 offset += scounter->seq_num * sizeof (guint64);
1291 return (char*)sinst + offset;
1295 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1297 SharedCounter *scounter;
1298 SharedInstance* inst;
1301 scounter = find_custom_counter (cat, counter);
1304 *type = simple_type_to_type [scounter->type];
1305 name = mono_string_to_utf8 (counter);
1306 inst = custom_get_instance (cat, scounter, name);
1310 return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1313 static const CategoryDesc*
1314 find_category (MonoString *category)
1317 for (i = 0; i < NUM_CATEGORIES; ++i) {
1318 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1319 return &predef_categories [i];
1325 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1326 MonoString* machine, int *type, MonoBoolean *custom)
1328 const CategoryDesc *cdesc;
1329 /* no support for counters on other machines */
1330 if (mono_string_compare_ascii (machine, "."))
1332 cdesc = find_category (category);
1334 SharedCategory *scat = find_custom_category (category);
1338 return custom_get_impl (scat, counter, instance, type);
1340 switch (cdesc->id) {
1342 return cpu_get_impl (counter, instance, type, custom);
1344 return process_get_impl (counter, instance, type, custom);
1345 case CATEGORY_MONO_MEM:
1346 return mono_mem_get_impl (counter, instance, type, custom);
1347 case CATEGORY_NETWORK:
1348 return network_get_impl (counter, instance, type, custom);
1352 case CATEGORY_REMOTING:
1353 case CATEGORY_LOADING:
1354 case CATEGORY_THREAD:
1355 case CATEGORY_INTEROP:
1356 case CATEGORY_SECURITY:
1357 case CATEGORY_ASPNET:
1358 case CATEGORY_THREADPOOL:
1359 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1365 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1367 ImplVtable *vtable = (ImplVtable *)impl;
1368 if (vtable && vtable->sample)
1369 return vtable->sample (vtable, only_value, sample);
1374 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1376 ImplVtable *vtable = (ImplVtable *)impl;
1377 if (vtable && vtable->update)
1378 return vtable->update (vtable, do_incr, value);
1383 mono_perfcounter_free_data (void *impl)
1385 ImplVtable *vtable = (ImplVtable *)impl;
1386 if (vtable && vtable->cleanup)
1387 vtable->cleanup (vtable);
1391 /* Category icalls */
1393 mono_perfcounter_category_del (MonoString *name)
1395 const CategoryDesc *cdesc;
1396 SharedCategory *cat;
1397 cdesc = find_category (name);
1398 /* can't delete a predefined category */
1402 cat = find_custom_category (name);
1403 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1404 if (!cat || cat->num_instances) {
1408 cat->header.ftype = FTYPE_DELETED;
1414 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1416 const CategoryDesc *cdesc;
1417 /* no support for counters on other machines */
1418 if (mono_string_compare_ascii (machine, "."))
1420 cdesc = find_category (category);
1422 SharedCategory *scat = find_custom_category (category);
1425 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1427 return mono_string_new (mono_domain_get (), cdesc->help);
1431 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1432 * TRUE only if a counter with that name exists in the category.
1435 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1437 const CategoryDesc *cdesc;
1438 /* no support for counters on other machines */
1439 if (mono_string_compare_ascii (machine, "."))
1441 cdesc = find_category (category);
1443 SharedCategory *scat = find_custom_category (category);
1446 /* counter is allowed to be null */
1449 /* search through the custom category */
1450 return find_custom_counter (scat, counter) != NULL;
1452 /* counter is allowed to be null */
1455 if (get_counter_in_category (cdesc, counter))
1460 /* C map of the type with the same name */
1466 } CounterCreationData;
1469 * Since we'll keep a copy of the category per-process, we should also make sure
1470 * categories with the same name are compatible.
1473 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1478 int num_counters = mono_array_length (items);
1479 int counters_data_size;
1482 char **counter_info = NULL;
1484 SharedCategory *cat;
1486 /* FIXME: ensure there isn't a category created already */
1487 mono_error_init (&error);
1488 name = mono_string_to_utf8_checked (category, &error);
1489 if (!mono_error_ok (&error))
1491 chelp = mono_string_to_utf8_checked (help, &error);
1492 if (!mono_error_ok (&error))
1494 counter_info = g_new0 (char*, num_counters * 2);
1495 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1496 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1497 for (i = 0; i < num_counters; ++i) {
1498 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1499 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1500 if (!mono_error_ok (&error))
1502 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1503 if (!mono_error_ok (&error))
1505 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1507 for (i = 0; i < num_counters * 2; ++i) {
1508 if (!counter_info [i])
1510 size += strlen (counter_info [i]) + 1;
1514 counters_data_size = num_counters * 8; /* optimize for size later */
1518 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1523 cat->num_counters = num_counters;
1524 cat->counters_data_size = counters_data_size;
1525 /* now copy the vaiable data */
1528 p += strlen (name) + 1;
1530 p += strlen (chelp) + 1;
1531 for (i = 0; i < num_counters; ++i) {
1532 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1533 /* emit the SharedCounter structures */
1534 *p++ = perfctr_type_compress (data->type);
1536 strcpy (p, counter_info [i * 2]);
1537 p += strlen (counter_info [i * 2]) + 1;
1538 strcpy (p, counter_info [i * 2 + 1]);
1539 p += strlen (counter_info [i * 2 + 1]) + 1;
1546 for (i = 0; i < num_counters * 2; ++i) {
1547 g_free (counter_info [i]);
1549 g_free (counter_info);
1553 mono_error_cleanup (&error);
1558 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1560 const CategoryDesc *cdesc;
1561 SharedInstance *sinst;
1563 /* no support for counters on other machines */
1564 /*FIXME: machine appears to be wrong
1565 if (mono_string_compare_ascii (machine, "."))
1567 cdesc = find_category (category);
1569 SharedCategory *scat;
1570 scat = find_custom_category (category);
1573 name = mono_string_to_utf8 (instance);
1574 sinst = find_custom_instance (scat, name);
1579 /* FIXME: search instance */
1585 mono_perfcounter_category_names (MonoString *machine)
1589 MonoDomain *domain = mono_domain_get ();
1590 GSList *custom_categories, *tmp;
1591 /* no support for counters on other machines */
1592 if (mono_string_compare_ascii (machine, "."))
1593 return mono_array_new (domain, mono_get_string_class (), 0);
1595 custom_categories = get_custom_categories ();
1596 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1597 for (i = 0; i < NUM_CATEGORIES; ++i) {
1598 const CategoryDesc *cdesc = &predef_categories [i];
1599 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1601 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1602 SharedCategory *scat = (SharedCategory *)tmp->data;
1603 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1607 g_slist_free (custom_categories);
1612 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1615 SharedCategory *scat;
1616 const CategoryDesc *cdesc;
1618 MonoDomain *domain = mono_domain_get ();
1619 /* no support for counters on other machines */
1620 if (mono_string_compare_ascii (machine, "."))
1621 return mono_array_new (domain, mono_get_string_class (), 0);
1622 cdesc = find_category (category);
1624 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1625 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1626 const CounterDesc *desc = &predef_counters [i];
1627 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1632 scat = find_custom_category (category);
1634 char *p = custom_category_counters (scat);
1636 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1637 for (i = 0; i < scat->num_counters; ++i) {
1638 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1639 p += 2; /* skip counter type */
1640 p += strlen (p) + 1; /* skip counter name */
1641 p += strlen (p) + 1; /* skip counter help */
1647 return mono_array_new (domain, mono_get_string_class (), 0);
1651 get_string_array (void **array, int count, gboolean is_process)
1654 MonoDomain *domain = mono_domain_get ();
1655 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1656 for (i = 0; i < count; ++i) {
1660 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1661 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1663 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1666 mono_array_setref (res, i, mono_string_new (domain, p));
1674 get_string_array_of_strings (void **array, int count)
1677 MonoDomain *domain = mono_domain_get ();
1678 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1679 for (i = 0; i < count; ++i) {
1680 char* p = (char *)array[i];
1681 mono_array_setref (res, i, mono_string_new (domain, p));
1688 get_mono_instances (void)
1697 buf = g_new (void*, count);
1698 res = mono_shared_area_instances (buf, count);
1699 } while (res == count);
1700 array = get_string_array (buf, res, TRUE);
1706 get_cpu_instances (void)
1712 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1713 buf = g_new (void*, count);
1714 for (i = 0; i < count; ++i)
1715 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1716 array = get_string_array (buf, count, FALSE);
1718 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1723 get_processes_instances (void)
1727 void **buf = mono_process_list (&count);
1729 return get_string_array (NULL, 0, FALSE);
1730 array = get_string_array (buf, count, TRUE);
1736 get_networkinterface_instances (void)
1740 void **buf = mono_networkinterface_list (&count);
1742 return get_string_array_of_strings (NULL, 0);
1743 array = get_string_array_of_strings (buf, count);
1744 g_strfreev ((char **) buf);
1749 get_custom_instances (MonoString *category)
1751 SharedCategory *scat;
1752 scat = find_custom_category (category);
1754 GSList *list = get_custom_instances_list (scat);
1757 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1758 for (tmp = list; tmp; tmp = tmp->next) {
1759 SharedInstance *inst = (SharedInstance *)tmp->data;
1760 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1763 g_slist_free (list);
1766 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1770 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1772 const CategoryDesc* cat;
1773 if (mono_string_compare_ascii (machine, "."))
1774 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1775 cat = find_category (category);
1777 return get_custom_instances (category);
1778 switch (cat->instance_type) {
1780 return get_mono_instances ();
1782 return get_cpu_instances ();
1783 case ProcessInstance:
1784 return get_processes_instances ();
1785 case NetworkInterfaceInstance:
1786 return get_networkinterface_instances ();
1787 case ThreadInstance:
1789 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1794 PerfCounterEnumCallback cb;
1796 } PerfCounterForeachData;
1799 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1805 SharedCategory *cat;
1806 SharedCounter *counter;
1807 SharedInstance *inst;
1808 PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1810 if (header->ftype == FTYPE_CATEGORY) {
1811 cat = (SharedCategory*)header;
1814 p += strlen (p) + 1; /* skip category name */
1815 p += strlen (p) + 1; /* skip category help */
1817 for (i = 0; i < cat->num_counters; ++i) {
1818 counter = (SharedCounter*) p;
1819 type = (unsigned char)*p++;
1820 /* seq_num = (int)* */ p++;
1822 p += strlen (p) + 1;
1824 p += strlen (p) + 1;
1826 inst = custom_get_instance (cat, counter, name);
1829 addr = custom_get_value_address (counter, inst);
1830 if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1839 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1841 PerfCounterForeachData foreach_data = { cb, data };
1845 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1852 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1854 g_assert_not_reached ();
1858 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1860 g_assert_not_reached ();
1864 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1866 g_assert_not_reached ();
1870 mono_perfcounter_free_data (void *impl)
1872 g_assert_not_reached ();
1875 /* Category icalls */
1877 mono_perfcounter_category_del (MonoString *name)
1879 g_assert_not_reached ();
1883 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1885 g_assert_not_reached ();
1889 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1891 g_assert_not_reached ();
1895 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1897 g_assert_not_reached ();
1901 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1903 g_assert_not_reached ();
1907 mono_perfcounter_category_names (MonoString *machine)
1909 g_assert_not_reached ();
1913 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1915 g_assert_not_reached ();
1919 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1921 g_assert_not_reached ();