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 vm_statistics_data_t vmstat;
502 if (KERN_SUCCESS != host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count)) {
503 g_warning ("Mono was unable to retrieve memory usage!");
507 return (guint64) vmstat.free_count;
509 #elif defined (HAVE_SYSCONF)
510 guint64 page_size = 0, num_pages = 0;
512 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
513 * reports invalid values, please add your OS specific code below. */
515 page_size = (guint64)sysconf (_SC_PAGESIZE);
518 #ifdef _SC_AVPHYS_PAGES
519 num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
522 if (!page_size || !num_pages) {
523 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
527 return page_size * num_pages;
534 mono_perfcounters_init (void)
536 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
540 mono_os_mutex_init_recursive (&perfctr_mutex);
542 shared_area = (MonoSharedArea *)mono_shared_area ();
543 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
544 shared_area->counters_size = sizeof (MonoPerfCounters);
545 shared_area->data_start = d_offset;
546 shared_area->size = 4096;
547 mono_perfcounters = &shared_area->counters;
551 perfctr_type_compress (int type)
554 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
555 if (simple_type_to_type [i] == type)
558 /* NumberOfItems32 */
563 shared_data_reserve_room (int size, int ftype)
565 SharedHeader* header;
566 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
567 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
572 unsigned short *next;
573 if (*p == FTYPE_END) {
574 if (size < (end - p))
580 next = (unsigned short*)(p + 2);
581 if (*p == FTYPE_DELETED) {
582 /* we reuse only if it's the same size */
592 header = (SharedHeader*)p;
593 header->ftype = ftype;
594 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
600 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
603 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
606 unsigned short *next;
609 next = (unsigned short*)(p + 2);
610 if (!func ((SharedHeader*)p, data))
619 foreach_shared_item (SharedFunc func, void *data)
621 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
622 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
624 foreach_shared_item_in_area (p, end, func, data);
628 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
630 /* FIXME: make this case insensitive */
631 guint16 *strc = mono_string_chars (str);
632 while (*strc == *ascii_str++) {
637 return *strc - *(const unsigned char *)(ascii_str - 1);
646 category_search (SharedHeader *header, void *data)
648 CatSearch *search = (CatSearch *)data;
649 if (header->ftype == FTYPE_CATEGORY) {
650 SharedCategory *cat = (SharedCategory*)header;
651 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
659 static SharedCategory*
660 find_custom_category (MonoString *name)
665 foreach_shared_item (category_search, &search);
670 category_collect (SharedHeader *header, void *data)
672 GSList **list = (GSList **)data;
673 if (header->ftype == FTYPE_CATEGORY) {
674 *list = g_slist_prepend (*list, header);
680 get_custom_categories (void) {
682 foreach_shared_item (category_collect, &list);
687 custom_category_counters (SharedCategory* cat)
689 char *p = cat->name + strlen (cat->name) + 1;
690 p += strlen (p) + 1; /* skip category help */
694 static SharedCounter*
695 find_custom_counter (SharedCategory* cat, MonoString *name)
698 char *p = custom_category_counters (cat);
699 for (i = 0; i < cat->num_counters; ++i) {
700 SharedCounter *counter = (SharedCounter*)p;
701 if (mono_string_compare_ascii (name, counter->name) == 0)
703 p += 2; /* skip counter type */
704 p += strlen (p) + 1; /* skip counter name */
705 p += strlen (p) + 1; /* skip counter help */
711 unsigned int cat_offset;
714 SharedInstance* result;
719 instance_search (SharedHeader *header, void *data)
721 InstanceSearch *search = (InstanceSearch *)data;
722 if (header->ftype == FTYPE_INSTANCE) {
723 SharedInstance *ins = (SharedInstance*)header;
724 if (search->cat_offset == ins->category_offset) {
726 if (strcmp (search->name, ins->instance_name) == 0) {
727 search->result = ins;
731 search->list = g_slist_prepend (search->list, ins);
738 static SharedInstance*
739 find_custom_instance (SharedCategory* cat, char *name)
741 InstanceSearch search;
742 search.cat_offset = (char*)cat - (char*)shared_area;
746 search.result = NULL;
747 foreach_shared_item (instance_search, &search);
748 return search.result;
752 get_custom_instances_list (SharedCategory* cat)
754 InstanceSearch search;
755 search.cat_offset = (char*)cat - (char*)shared_area;
759 search.result = NULL;
760 foreach_shared_item (instance_search, &search);
765 custom_category_help (SharedCategory* cat)
767 return cat->name + strlen (cat->name) + 1;
770 static const CounterDesc*
771 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
773 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
774 const CounterDesc *end = &predef_counters [desc [1].first_counter];
775 for (; cdesc < end; ++cdesc) {
776 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
782 /* fill the info in sample (except the raw value) */
784 fill_sample (MonoCounterSample *sample)
786 sample->timeStamp = mono_100ns_ticks ();
787 sample->timeStamp100nSec = sample->timeStamp;
788 sample->counterTimeStamp = sample->timeStamp;
789 sample->counterFrequency = 10000000;
790 sample->systemFrequency = 10000000;
791 // the real basevalue needs to be get from a different counter...
792 sample->baseValue = 0;
796 id_from_string (MonoString *instance, gboolean is_process)
799 if (mono_string_length (instance)) {
800 char *id_str = mono_string_to_utf8 (instance);
802 id = strtol (id_str, &end, 0);
803 if (end == id_str && !is_process)
811 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
813 MonoProcessError error;
814 int id = GPOINTER_TO_INT (vtable->arg);
818 fill_sample (sample);
819 sample->baseValue = 1;
821 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
823 case COUNTER_CPU_USER_TIME:
824 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
826 case COUNTER_CPU_PRIV_TIME:
827 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
829 case COUNTER_CPU_INTR_TIME:
830 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
832 case COUNTER_CPU_DCP_TIME:
833 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
835 case COUNTER_CPU_PROC_TIME:
836 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
843 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
845 int id = id_from_string (instance, FALSE) << 5;
846 const CounterDesc *cdesc;
848 /* increase the shift above and the mask also in the implementation functions */
849 //g_assert (32 > desc [1].first_counter - desc->first_counter);
850 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
852 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
858 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
860 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
861 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
863 fill_sample (sample);
866 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
868 case COUNTER_NETWORK_BYTESRECSEC:
869 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
871 case COUNTER_NETWORK_BYTESSENTSEC:
872 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
874 case COUNTER_NETWORK_BYTESTOTALSEC:
875 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
879 if (error == MONO_NETWORK_ERROR_NONE)
886 network_cleanup (ImplVtable *vtable)
888 NetworkVtableArg *narg;
893 narg = (NetworkVtableArg *)vtable->arg;
904 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
906 const CounterDesc *cdesc;
907 NetworkVtableArg *narg;
912 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
913 instance_name = mono_string_to_utf8 (instance);
914 narg = g_new0 (NetworkVtableArg, 1);
915 narg->id = cdesc->id;
916 narg->name = instance_name;
918 vtable = create_vtable (narg, get_network_counter, NULL);
919 vtable->cleanup = network_cleanup;
926 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
928 int id = GPOINTER_TO_INT (vtable->arg);
934 fill_sample (sample);
935 sample->baseValue = 1;
937 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
939 case COUNTER_PROC_USER_TIME:
940 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
942 case COUNTER_PROC_PRIV_TIME:
943 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
945 case COUNTER_PROC_PROC_TIME:
946 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
948 case COUNTER_PROC_THREADS:
949 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
951 case COUNTER_PROC_VBYTES:
952 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
954 case COUNTER_PROC_WSET:
955 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
957 case COUNTER_PROC_PBYTES:
958 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
965 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
967 int id = id_from_string (instance, TRUE) << 5;
968 const CounterDesc *cdesc;
970 /* increase the shift above and the mask also in the implementation functions */
971 //g_assert (32 > desc [1].first_counter - desc->first_counter);
972 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
974 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
980 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
982 int id = GPOINTER_TO_INT (vtable->arg);
984 fill_sample (sample);
985 sample->baseValue = 1;
987 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
989 case COUNTER_MEM_NUM_OBJECTS:
990 sample->rawValue = 0;
992 case COUNTER_MEM_PHYS_TOTAL:
993 sample->rawValue = mono_determine_physical_ram_size ();;
995 case COUNTER_MEM_PHYS_AVAILABLE:
996 sample->rawValue = mono_determine_physical_ram_available_size ();;
1003 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1005 const CounterDesc *cdesc;
1007 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1008 *type = cdesc->type;
1009 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1015 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1017 PredefVtable *vt = (PredefVtable *)vtable;
1018 const CounterDesc *desc;
1019 int cat_id = GPOINTER_TO_INT (vtable->arg);
1020 int id = cat_id >> 16;
1023 fill_sample (sample);
1024 sample->baseValue = 1;
1026 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1027 sample->counterType = desc->type;
1028 /* FIXME: check that the offset fits inside imported counters */
1029 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1030 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1035 predef_vtable (void *arg, MonoString *instance)
1037 MonoSharedArea *area;
1038 PredefVtable *vtable;
1039 char *pids = mono_string_to_utf8 (instance);
1044 area = load_sarea_for_pid (pid);
1048 vtable = g_new (PredefVtable, 1);
1049 vtable->vtable.arg = arg;
1050 vtable->vtable.sample = predef_readonly_counter;
1051 vtable->vtable.cleanup = predef_cleanup;
1052 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1055 return (ImplVtable*)vtable;
1058 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1059 * this needs some way to set sample->counterType as well, though.
1062 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1064 int cat_id = GPOINTER_TO_INT (vtable->arg);
1065 int id = cat_id >> 16;
1068 fill_sample (sample);
1069 sample->baseValue = 1;
1071 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1075 case COUNTER_EXC_THROWN:
1076 sample->rawValue = mono_perfcounters->exceptions_thrown;
1080 case CATEGORY_ASPNET:
1082 case COUNTER_ASPNET_REQ_Q:
1083 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1085 case COUNTER_ASPNET_REQ_TOTAL:
1086 sample->rawValue = mono_perfcounters->aspnet_requests;
1090 case CATEGORY_THREADPOOL:
1092 case COUNTER_THREADPOOL_WORKITEMS:
1093 sample->rawValue = mono_perfcounters->threadpool_workitems;
1095 case COUNTER_THREADPOOL_IOWORKITEMS:
1096 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1098 case COUNTER_THREADPOOL_THREADS:
1099 sample->rawValue = mono_perfcounters->threadpool_threads;
1101 case COUNTER_THREADPOOL_IOTHREADS:
1102 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1108 case COUNTER_JIT_BYTES:
1109 sample->rawValue = mono_perfcounters->jit_bytes;
1111 case COUNTER_JIT_METHODS:
1112 sample->rawValue = mono_perfcounters->jit_methods;
1114 case COUNTER_JIT_TIME:
1115 sample->rawValue = mono_perfcounters->jit_time;
1117 case COUNTER_JIT_BYTES_PSEC:
1118 sample->rawValue = mono_perfcounters->jit_bytes;
1120 case COUNTER_JIT_FAILURES:
1121 sample->rawValue = mono_perfcounters->jit_failures;
1130 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1132 guint32 *volatile ptr = NULL;
1133 gint64 *volatile ptr64 = NULL;
1134 int cat_id = GPOINTER_TO_INT (vtable->arg);
1135 int id = cat_id >> 16;
1138 case CATEGORY_ASPNET:
1140 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1141 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1144 case CATEGORY_THREADPOOL:
1146 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1147 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1148 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1149 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1156 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1158 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1163 /* this can be non-atomic */
1168 /* FIXME: we need to do this atomically */
1169 /* No InterlockedIncrement64() yet */
1172 return InterlockedIncrement64 (ptr);
1174 return InterlockedDecrement64 (ptr);
1180 /* this can be non-atomic */
1188 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1190 const CounterDesc *cdesc;
1192 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1193 *type = cdesc->type;
1194 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1195 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1197 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1203 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1205 CustomVTable *counter_data = (CustomVTable *)vtable;
1207 fill_sample (sample);
1208 sample->baseValue = 1;
1210 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1212 sample->rawValue = 0;
1214 sample->rawValue = *(guint64*)vtable->arg;
1219 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1221 /* FIXME: check writability */
1222 guint64 *ptr = (guint64 *)vtable->arg;
1225 /* FIXME: we need to do this atomically */
1229 /* this can be non-atomic */
1236 static SharedInstance*
1237 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1239 SharedInstance* inst;
1242 inst = find_custom_instance (cat, name);
1245 size = sizeof (SharedInstance) + strlen (name);
1248 size += (sizeof (guint64) * cat->num_counters);
1250 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1256 inst->category_offset = (char*)cat - (char*)shared_area;
1257 cat->num_instances++;
1258 /* now copy the variable data */
1259 p = inst->instance_name;
1261 p += strlen (name) + 1;
1268 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1270 CustomVTable* vtable;
1271 vtable = g_new0 (CustomVTable, 1);
1272 vtable->vtable.arg = data;
1273 vtable->vtable.sample = custom_writable_counter;
1274 vtable->vtable.update = custom_writable_update;
1275 vtable->instance_desc = inst;
1276 vtable->counter_desc = scounter;
1278 return (ImplVtable*)vtable;
1282 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1284 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1287 offset += scounter->seq_num * sizeof (guint64);
1288 return (char*)sinst + offset;
1292 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1294 SharedCounter *scounter;
1295 SharedInstance* inst;
1298 scounter = find_custom_counter (cat, counter);
1301 *type = simple_type_to_type [scounter->type];
1302 name = mono_string_to_utf8 (counter);
1303 inst = custom_get_instance (cat, scounter, name);
1307 return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1310 static const CategoryDesc*
1311 find_category (MonoString *category)
1314 for (i = 0; i < NUM_CATEGORIES; ++i) {
1315 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1316 return &predef_categories [i];
1322 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1323 MonoString* machine, int *type, MonoBoolean *custom)
1325 const CategoryDesc *cdesc;
1326 /* no support for counters on other machines */
1327 if (mono_string_compare_ascii (machine, "."))
1329 cdesc = find_category (category);
1331 SharedCategory *scat = find_custom_category (category);
1335 return custom_get_impl (scat, counter, instance, type);
1337 switch (cdesc->id) {
1339 return cpu_get_impl (counter, instance, type, custom);
1341 return process_get_impl (counter, instance, type, custom);
1342 case CATEGORY_MONO_MEM:
1343 return mono_mem_get_impl (counter, instance, type, custom);
1344 case CATEGORY_NETWORK:
1345 return network_get_impl (counter, instance, type, custom);
1349 case CATEGORY_REMOTING:
1350 case CATEGORY_LOADING:
1351 case CATEGORY_THREAD:
1352 case CATEGORY_INTEROP:
1353 case CATEGORY_SECURITY:
1354 case CATEGORY_ASPNET:
1355 case CATEGORY_THREADPOOL:
1356 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1362 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1364 ImplVtable *vtable = (ImplVtable *)impl;
1365 if (vtable && vtable->sample)
1366 return vtable->sample (vtable, only_value, sample);
1371 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1373 ImplVtable *vtable = (ImplVtable *)impl;
1374 if (vtable && vtable->update)
1375 return vtable->update (vtable, do_incr, value);
1380 mono_perfcounter_free_data (void *impl)
1382 ImplVtable *vtable = (ImplVtable *)impl;
1383 if (vtable && vtable->cleanup)
1384 vtable->cleanup (vtable);
1388 /* Category icalls */
1390 mono_perfcounter_category_del (MonoString *name)
1392 const CategoryDesc *cdesc;
1393 SharedCategory *cat;
1394 cdesc = find_category (name);
1395 /* can't delete a predefined category */
1399 cat = find_custom_category (name);
1400 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1401 if (!cat || cat->num_instances) {
1405 cat->header.ftype = FTYPE_DELETED;
1411 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1413 const CategoryDesc *cdesc;
1414 /* no support for counters on other machines */
1415 if (mono_string_compare_ascii (machine, "."))
1417 cdesc = find_category (category);
1419 SharedCategory *scat = find_custom_category (category);
1422 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1424 return mono_string_new (mono_domain_get (), cdesc->help);
1428 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1429 * TRUE only if a counter with that name exists in the category.
1432 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1434 const CategoryDesc *cdesc;
1435 /* no support for counters on other machines */
1436 if (mono_string_compare_ascii (machine, "."))
1438 cdesc = find_category (category);
1440 SharedCategory *scat = find_custom_category (category);
1443 /* counter is allowed to be null */
1446 /* search through the custom category */
1447 return find_custom_counter (scat, counter) != NULL;
1449 /* counter is allowed to be null */
1452 if (get_counter_in_category (cdesc, counter))
1457 /* C map of the type with the same name */
1463 } CounterCreationData;
1466 * Since we'll keep a copy of the category per-process, we should also make sure
1467 * categories with the same name are compatible.
1470 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1475 int num_counters = mono_array_length (items);
1476 int counters_data_size;
1479 char **counter_info = NULL;
1481 SharedCategory *cat;
1483 /* FIXME: ensure there isn't a category created already */
1484 mono_error_init (&error);
1485 name = mono_string_to_utf8_checked (category, &error);
1486 if (!mono_error_ok (&error))
1488 chelp = mono_string_to_utf8_checked (help, &error);
1489 if (!mono_error_ok (&error))
1491 counter_info = g_new0 (char*, num_counters * 2);
1492 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1493 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1494 for (i = 0; i < num_counters; ++i) {
1495 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1496 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1497 if (!mono_error_ok (&error))
1499 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1500 if (!mono_error_ok (&error))
1502 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1504 for (i = 0; i < num_counters * 2; ++i) {
1505 if (!counter_info [i])
1507 size += strlen (counter_info [i]) + 1;
1511 counters_data_size = num_counters * 8; /* optimize for size later */
1515 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1520 cat->num_counters = num_counters;
1521 cat->counters_data_size = counters_data_size;
1522 /* now copy the vaiable data */
1525 p += strlen (name) + 1;
1527 p += strlen (chelp) + 1;
1528 for (i = 0; i < num_counters; ++i) {
1529 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1530 /* emit the SharedCounter structures */
1531 *p++ = perfctr_type_compress (data->type);
1533 strcpy (p, counter_info [i * 2]);
1534 p += strlen (counter_info [i * 2]) + 1;
1535 strcpy (p, counter_info [i * 2 + 1]);
1536 p += strlen (counter_info [i * 2 + 1]) + 1;
1543 for (i = 0; i < num_counters * 2; ++i) {
1544 g_free (counter_info [i]);
1546 g_free (counter_info);
1550 mono_error_cleanup (&error);
1555 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1557 const CategoryDesc *cdesc;
1558 SharedInstance *sinst;
1560 /* no support for counters on other machines */
1561 /*FIXME: machine appears to be wrong
1562 if (mono_string_compare_ascii (machine, "."))
1564 cdesc = find_category (category);
1566 SharedCategory *scat;
1567 scat = find_custom_category (category);
1570 name = mono_string_to_utf8 (instance);
1571 sinst = find_custom_instance (scat, name);
1576 /* FIXME: search instance */
1582 mono_perfcounter_category_names (MonoString *machine)
1586 MonoDomain *domain = mono_domain_get ();
1587 GSList *custom_categories, *tmp;
1588 /* no support for counters on other machines */
1589 if (mono_string_compare_ascii (machine, "."))
1590 return mono_array_new (domain, mono_get_string_class (), 0);
1592 custom_categories = get_custom_categories ();
1593 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1594 for (i = 0; i < NUM_CATEGORIES; ++i) {
1595 const CategoryDesc *cdesc = &predef_categories [i];
1596 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1598 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1599 SharedCategory *scat = (SharedCategory *)tmp->data;
1600 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1604 g_slist_free (custom_categories);
1609 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1612 SharedCategory *scat;
1613 const CategoryDesc *cdesc;
1615 MonoDomain *domain = mono_domain_get ();
1616 /* no support for counters on other machines */
1617 if (mono_string_compare_ascii (machine, "."))
1618 return mono_array_new (domain, mono_get_string_class (), 0);
1619 cdesc = find_category (category);
1621 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1622 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1623 const CounterDesc *desc = &predef_counters [i];
1624 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1629 scat = find_custom_category (category);
1631 char *p = custom_category_counters (scat);
1633 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1634 for (i = 0; i < scat->num_counters; ++i) {
1635 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1636 p += 2; /* skip counter type */
1637 p += strlen (p) + 1; /* skip counter name */
1638 p += strlen (p) + 1; /* skip counter help */
1644 return mono_array_new (domain, mono_get_string_class (), 0);
1648 get_string_array (void **array, int count, gboolean is_process)
1651 MonoDomain *domain = mono_domain_get ();
1652 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1653 for (i = 0; i < count; ++i) {
1657 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1658 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1660 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1663 mono_array_setref (res, i, mono_string_new (domain, p));
1671 get_string_array_of_strings (void **array, int count)
1674 MonoDomain *domain = mono_domain_get ();
1675 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1676 for (i = 0; i < count; ++i) {
1677 char* p = (char *)array[i];
1678 mono_array_setref (res, i, mono_string_new (domain, p));
1685 get_mono_instances (void)
1694 buf = g_new (void*, count);
1695 res = mono_shared_area_instances (buf, count);
1696 } while (res == count);
1697 array = get_string_array (buf, res, TRUE);
1703 get_cpu_instances (void)
1709 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1710 buf = g_new (void*, count);
1711 for (i = 0; i < count; ++i)
1712 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1713 array = get_string_array (buf, count, FALSE);
1715 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1720 get_processes_instances (void)
1724 void **buf = mono_process_list (&count);
1726 return get_string_array (NULL, 0, FALSE);
1727 array = get_string_array (buf, count, TRUE);
1733 get_networkinterface_instances (void)
1737 void **buf = mono_networkinterface_list (&count);
1739 return get_string_array_of_strings (NULL, 0);
1740 array = get_string_array_of_strings (buf, count);
1741 g_strfreev ((char **) buf);
1746 get_custom_instances (MonoString *category)
1748 SharedCategory *scat;
1749 scat = find_custom_category (category);
1751 GSList *list = get_custom_instances_list (scat);
1754 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1755 for (tmp = list; tmp; tmp = tmp->next) {
1756 SharedInstance *inst = (SharedInstance *)tmp->data;
1757 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1760 g_slist_free (list);
1763 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1767 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1769 const CategoryDesc* cat;
1770 if (mono_string_compare_ascii (machine, "."))
1771 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1772 cat = find_category (category);
1774 return get_custom_instances (category);
1775 switch (cat->instance_type) {
1777 return get_mono_instances ();
1779 return get_cpu_instances ();
1780 case ProcessInstance:
1781 return get_processes_instances ();
1782 case NetworkInterfaceInstance:
1783 return get_networkinterface_instances ();
1784 case ThreadInstance:
1786 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1791 PerfCounterEnumCallback cb;
1793 } PerfCounterForeachData;
1796 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1802 SharedCategory *cat;
1803 SharedCounter *counter;
1804 SharedInstance *inst;
1805 PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1807 if (header->ftype == FTYPE_CATEGORY) {
1808 cat = (SharedCategory*)header;
1811 p += strlen (p) + 1; /* skip category name */
1812 p += strlen (p) + 1; /* skip category help */
1814 for (i = 0; i < cat->num_counters; ++i) {
1815 counter = (SharedCounter*) p;
1816 type = (unsigned char)*p++;
1817 /* seq_num = (int)* */ p++;
1819 p += strlen (p) + 1;
1821 p += strlen (p) + 1;
1823 inst = custom_get_instance (cat, counter, name);
1826 addr = custom_get_value_address (counter, inst);
1827 if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1836 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1838 PerfCounterForeachData foreach_data = { cb, data };
1842 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1849 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1851 g_assert_not_reached ();
1855 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1857 g_assert_not_reached ();
1861 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1863 g_assert_not_reached ();
1867 mono_perfcounter_free_data (void *impl)
1869 g_assert_not_reached ();
1872 /* Category icalls */
1874 mono_perfcounter_category_del (MonoString *name)
1876 g_assert_not_reached ();
1880 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1882 g_assert_not_reached ();
1886 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1888 g_assert_not_reached ();
1892 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1894 g_assert_not_reached ();
1898 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1900 g_assert_not_reached ();
1904 mono_perfcounter_category_names (MonoString *machine)
1906 g_assert_not_reached ();
1910 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1912 g_assert_not_reached ();
1916 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1918 g_assert_not_reached ();