4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #if defined (__OpenBSD__)
21 #include <sys/param.h>
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
26 #ifdef HAVE_SYS_TIME_H
29 #if defined (__APPLE__)
30 #include <mach/message.h>
31 #include <mach/mach_host.h>
32 #include <mach/host_info.h>
34 #if defined (__NetBSD__) || defined (__APPLE__)
35 #include <sys/sysctl.h>
37 #include "metadata/mono-perfcounters.h"
38 #include "metadata/appdomain.h"
39 #include "metadata/object-internals.h"
41 #include "metadata/class-internals.h"
42 #include "utils/mono-time.h"
43 #include "utils/mono-mmap.h"
44 #include "utils/mono-proclib.h"
45 #include "utils/mono-networkinterfaces.h"
46 #include "utils/mono-error-internals.h"
47 #include "utils/atomic.h"
48 #include <mono/io-layer/io-layer.h>
50 /* map of CounterSample.cs */
51 struct _MonoCounterSample {
54 gint64 counterFrequency;
55 gint64 systemFrequency;
57 gint64 timeStamp100nSec;
58 gint64 counterTimeStamp;
62 #ifndef DISABLE_PERFCOUNTERS
63 /* map of PerformanceCounterType.cs */
65 NumberOfItemsHEX32=0x00000000,
66 NumberOfItemsHEX64=0x00000100,
67 NumberOfItems32=0x00010000,
68 NumberOfItems64=0x00010100,
69 CounterDelta32=0x00400400,
70 CounterDelta64=0x00400500,
71 SampleCounter=0x00410400,
72 CountPerTimeInterval32=0x00450400,
73 CountPerTimeInterval64=0x00450500,
74 RateOfCountsPerSecond32=0x10410400,
75 RateOfCountsPerSecond64=0x10410500,
76 RawFraction=0x20020400,
77 CounterTimer=0x20410500,
78 Timer100Ns=0x20510500,
79 SampleFraction=0x20C20400,
80 CounterTimerInverse=0x21410500,
81 Timer100NsInverse=0x21510500,
82 CounterMultiTimer=0x22410500,
83 CounterMultiTimer100Ns=0x22510500,
84 CounterMultiTimerInverse=0x23410500,
85 CounterMultiTimer100NsInverse=0x23510500,
86 AverageTimer32=0x30020400,
87 ElapsedTime=0x30240500,
88 AverageCount64=0x40020500,
89 SampleBase=0x40030401,
90 AverageBase=0x40030402,
92 CounterMultiBase=0x42030500
95 /* maps a small integer type to the counter types above */
97 simple_type_to_type [] = {
98 NumberOfItemsHEX32, NumberOfItemsHEX64,
99 NumberOfItems32, NumberOfItems64,
100 CounterDelta32, CounterDelta64,
101 SampleCounter, CountPerTimeInterval32,
102 CountPerTimeInterval64, RateOfCountsPerSecond32,
103 RateOfCountsPerSecond64, RawFraction,
104 CounterTimer, Timer100Ns,
105 SampleFraction, CounterTimerInverse,
106 Timer100NsInverse, CounterMultiTimer,
107 CounterMultiTimer100Ns, CounterMultiTimerInverse,
108 CounterMultiTimer100NsInverse, AverageTimer32,
109 ElapsedTime, AverageCount64,
110 SampleBase, AverageBase,
111 RawBase, CounterMultiBase
125 NetworkInterfaceInstance,
129 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
130 #define PERFCTR_COUNTER(id,name,help,type,field)
132 #include "mono-perfcounters-def.h"
137 #undef PERFCTR_COUNTER
138 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
139 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
140 /* each counter is assigned an id starting from 0 inside the category */
142 #include "mono-perfcounters-def.h"
147 #undef PERFCTR_COUNTER
148 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
149 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
150 /* this is used just to count the number of counters */
152 #include "mono-perfcounters-def.h"
156 static mono_mutex_t perfctr_mutex;
157 #define perfctr_lock() mono_os_mutex_lock (&perfctr_mutex)
158 #define perfctr_unlock() mono_os_mutex_unlock (&perfctr_mutex)
163 unsigned short counters_start;
164 unsigned short counters_size;
165 unsigned short data_start;
166 MonoPerfCounters counters;
171 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
173 struct stanza_header {
174 byte stanza_type; // FTYPE_*
176 ushort stanza_length; // includeas header
181 // perfcat and perfinstance are 4-bytes aligned
185 ushort length; // includes the counters
187 ushort counters_data_size;
189 char name[]; // null terminated
190 char help[]; // null terminated
191 // perfcounters follow
194 char name[]; // null terminated
195 char help[]; // null terminated
200 struct perfinstance {
202 byte data_offset; // offset of counters from beginning of struct
204 uint category_offset; // offset of category in the shared area
205 char name[]; // null terminated
206 // data follows: this is always 8-byte aligned
212 FTYPE_CATEGORY = 'C',
214 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
215 FTYPE_INSTANCE = 'I',
228 unsigned short num_counters;
229 unsigned short counters_data_size;
231 /* variable length data follows */
235 // SharedCounter counters_info [num_counters]
240 unsigned int category_offset;
241 /* variable length data follows */
242 char instance_name [1];
249 /* variable length data follows */
260 unsigned int instance_type : 6;
268 unsigned short offset; // offset inside MonoPerfCounters
273 #undef PERFCTR_COUNTER
274 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
275 #define PERFCTR_COUNTER(id,name,help,type,field)
276 static const CategoryDesc
277 predef_categories [] = {
278 #include "mono-perfcounters-def.h"
279 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
283 #undef PERFCTR_COUNTER
284 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
285 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
286 static const CounterDesc
287 predef_counters [] = {
288 #include "mono-perfcounters-def.h"
289 {NULL, NULL, -1, 0, 0}
293 * We have several different classes of counters:
295 * *) runtime counters
297 * *) user-defined counters
298 * *) windows counters (the implementation on windows will use this)
300 * To easily handle the differences we create a vtable for each class that contains the
301 * function pointers with the actual implementation to access the counters.
303 typedef struct _ImplVtable ImplVtable;
305 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
306 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
307 typedef void (*CleanupFunc) (ImplVtable *vtable);
323 MonoPerfCounters *counters;
329 SharedInstance *instance_desc;
330 SharedCounter *counter_desc;
334 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
336 ImplVtable *vtable = g_new0 (ImplVtable, 1);
338 vtable->sample = sample;
339 vtable->update = update;
343 MonoPerfCounters *mono_perfcounters = NULL;
344 static MonoSharedArea *shared_area = NULL;
351 /* maps a pid to a ExternalSArea pointer */
352 static GHashTable *pid_to_shared_area = NULL;
354 static MonoSharedArea *
355 load_sarea_for_pid (int pid)
358 MonoSharedArea *area = NULL;
361 if (pid_to_shared_area == NULL)
362 pid_to_shared_area = g_hash_table_new (NULL, NULL);
363 data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
365 area = (MonoSharedArea *)mono_shared_area_for_pid (GINT_TO_POINTER (pid));
367 data = g_new (ExternalSArea, 1);
370 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
373 area = (MonoSharedArea *)data->sarea;
381 unref_pid_unlocked (int pid)
384 data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
387 if (!data->refcount) {
388 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
389 mono_shared_area_unload (data->sarea);
396 predef_cleanup (ImplVtable *vtable)
398 PredefVtable *vt = (PredefVtable*)vtable;
399 /* ExternalSArea *data; */
402 if (!pid_to_shared_area) {
406 unref_pid_unlocked (vt->pid);
411 mono_determine_physical_ram_size (void)
413 #if defined (TARGET_WIN32)
414 MEMORYSTATUSEX memstat;
416 memstat.dwLength = sizeof (memstat);
417 GlobalMemoryStatusEx (&memstat);
418 return (guint64)memstat.ullTotalPhys;
419 #elif defined (__NetBSD__) || defined (__APPLE__)
433 size_t size_sys = sizeof (value);
435 sysctl (mib, 2, &value, &size_sys, NULL, 0);
439 return (guint64)value;
440 #elif defined (HAVE_SYSCONF)
441 guint64 page_size = 0, num_pages = 0;
443 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
444 * reports invalid values, please add your OS specific code below. */
446 page_size = (guint64)sysconf (_SC_PAGESIZE);
449 #ifdef _SC_PHYS_PAGES
450 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
453 if (!page_size || !num_pages) {
454 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
458 return page_size * num_pages;
465 mono_determine_physical_ram_available_size (void)
467 #if defined (TARGET_WIN32)
468 MEMORYSTATUSEX memstat;
470 memstat.dwLength = sizeof (memstat);
471 GlobalMemoryStatusEx (&memstat);
472 return (guint64)memstat.ullAvailPhys;
474 #elif defined (__NetBSD__)
475 struct vmtotal vm_total;
483 #if defined (VM_METER)
489 len = sizeof (vm_total);
490 sysctl (mib, 2, &vm_total, &len, NULL, 0);
496 len = sizeof (page_size);
497 sysctl (mib, 2, &page_size, &len, NULL, 0
499 return ((guint64) value.t_free * page_size) / 1024;
500 #elif defined (__APPLE__)
501 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
502 mach_port_t host = mach_host_self();
504 vm_statistics_data_t vmstat;
505 if (KERN_SUCCESS != host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &count)) {
506 g_warning ("Mono was unable to retrieve memory usage!");
510 host_page_size(host, &page_size);
511 return (guint64) vmstat.free_count * page_size;
513 #elif defined (HAVE_SYSCONF)
514 guint64 page_size = 0, num_pages = 0;
516 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
517 * reports invalid values, please add your OS specific code below. */
519 page_size = (guint64)sysconf (_SC_PAGESIZE);
522 #ifdef _SC_AVPHYS_PAGES
523 num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
526 if (!page_size || !num_pages) {
527 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
531 return page_size * num_pages;
538 mono_perfcounters_init (void)
540 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
544 mono_os_mutex_init_recursive (&perfctr_mutex);
546 shared_area = (MonoSharedArea *)mono_shared_area ();
547 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
548 shared_area->counters_size = sizeof (MonoPerfCounters);
549 shared_area->data_start = d_offset;
550 shared_area->size = 4096;
551 mono_perfcounters = &shared_area->counters;
555 perfctr_type_compress (int type)
558 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
559 if (simple_type_to_type [i] == type)
562 /* NumberOfItems32 */
567 shared_data_reserve_room (int size, int ftype)
569 SharedHeader* header;
570 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
571 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
576 unsigned short *next;
577 if (*p == FTYPE_END) {
578 if (size < (end - p))
584 next = (unsigned short*)(p + 2);
585 if (*p == FTYPE_DELETED) {
586 /* we reuse only if it's the same size */
596 header = (SharedHeader*)p;
597 header->ftype = ftype;
598 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
604 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
607 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
610 unsigned short *next;
613 next = (unsigned short*)(p + 2);
614 if (!func ((SharedHeader*)p, data))
623 foreach_shared_item (SharedFunc func, void *data)
625 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
626 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
628 foreach_shared_item_in_area (p, end, func, data);
632 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
634 /* FIXME: make this case insensitive */
635 guint16 *strc = mono_string_chars (str);
636 while (*strc == *ascii_str++) {
641 return *strc - *(const unsigned char *)(ascii_str - 1);
650 category_search (SharedHeader *header, void *data)
652 CatSearch *search = (CatSearch *)data;
653 if (header->ftype == FTYPE_CATEGORY) {
654 SharedCategory *cat = (SharedCategory*)header;
655 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
663 static SharedCategory*
664 find_custom_category (MonoString *name)
669 foreach_shared_item (category_search, &search);
674 category_collect (SharedHeader *header, void *data)
676 GSList **list = (GSList **)data;
677 if (header->ftype == FTYPE_CATEGORY) {
678 *list = g_slist_prepend (*list, header);
684 get_custom_categories (void) {
686 foreach_shared_item (category_collect, &list);
691 custom_category_counters (SharedCategory* cat)
693 char *p = cat->name + strlen (cat->name) + 1;
694 p += strlen (p) + 1; /* skip category help */
698 static SharedCounter*
699 find_custom_counter (SharedCategory* cat, MonoString *name)
702 char *p = custom_category_counters (cat);
703 for (i = 0; i < cat->num_counters; ++i) {
704 SharedCounter *counter = (SharedCounter*)p;
705 if (mono_string_compare_ascii (name, counter->name) == 0)
707 p += 2; /* skip counter type */
708 p += strlen (p) + 1; /* skip counter name */
709 p += strlen (p) + 1; /* skip counter help */
715 unsigned int cat_offset;
718 SharedInstance* result;
723 instance_search (SharedHeader *header, void *data)
725 InstanceSearch *search = (InstanceSearch *)data;
726 if (header->ftype == FTYPE_INSTANCE) {
727 SharedInstance *ins = (SharedInstance*)header;
728 if (search->cat_offset == ins->category_offset) {
730 if (strcmp (search->name, ins->instance_name) == 0) {
731 search->result = ins;
735 search->list = g_slist_prepend (search->list, ins);
742 static SharedInstance*
743 find_custom_instance (SharedCategory* cat, char *name)
745 InstanceSearch search;
746 search.cat_offset = (char*)cat - (char*)shared_area;
750 search.result = NULL;
751 foreach_shared_item (instance_search, &search);
752 return search.result;
756 get_custom_instances_list (SharedCategory* cat)
758 InstanceSearch search;
759 search.cat_offset = (char*)cat - (char*)shared_area;
763 search.result = NULL;
764 foreach_shared_item (instance_search, &search);
769 custom_category_help (SharedCategory* cat)
771 return cat->name + strlen (cat->name) + 1;
774 static const CounterDesc*
775 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
777 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
778 const CounterDesc *end = &predef_counters [desc [1].first_counter];
779 for (; cdesc < end; ++cdesc) {
780 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
786 /* fill the info in sample (except the raw value) */
788 fill_sample (MonoCounterSample *sample)
790 sample->timeStamp = mono_100ns_ticks ();
791 sample->timeStamp100nSec = sample->timeStamp;
792 sample->counterTimeStamp = sample->timeStamp;
793 sample->counterFrequency = 10000000;
794 sample->systemFrequency = 10000000;
795 // the real basevalue needs to be get from a different counter...
796 sample->baseValue = 0;
800 id_from_string (MonoString *instance, gboolean is_process)
803 if (mono_string_length (instance)) {
804 char *id_str = mono_string_to_utf8 (instance);
806 id = strtol (id_str, &end, 0);
807 if (end == id_str && !is_process)
815 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
817 MonoProcessError error;
818 int id = GPOINTER_TO_INT (vtable->arg);
822 fill_sample (sample);
823 sample->baseValue = 1;
825 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
827 case COUNTER_CPU_USER_TIME:
828 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
830 case COUNTER_CPU_PRIV_TIME:
831 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
833 case COUNTER_CPU_INTR_TIME:
834 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
836 case COUNTER_CPU_DCP_TIME:
837 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
839 case COUNTER_CPU_PROC_TIME:
840 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
847 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
849 int id = id_from_string (instance, FALSE) << 5;
850 const CounterDesc *cdesc;
852 /* increase the shift above and the mask also in the implementation functions */
853 //g_assert (32 > desc [1].first_counter - desc->first_counter);
854 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
856 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
862 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
864 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
865 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
867 fill_sample (sample);
870 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
872 case COUNTER_NETWORK_BYTESRECSEC:
873 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
875 case COUNTER_NETWORK_BYTESSENTSEC:
876 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
878 case COUNTER_NETWORK_BYTESTOTALSEC:
879 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
883 if (error == MONO_NETWORK_ERROR_NONE)
890 network_cleanup (ImplVtable *vtable)
892 NetworkVtableArg *narg;
897 narg = (NetworkVtableArg *)vtable->arg;
908 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
910 const CounterDesc *cdesc;
911 NetworkVtableArg *narg;
916 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
917 instance_name = mono_string_to_utf8 (instance);
918 narg = g_new0 (NetworkVtableArg, 1);
919 narg->id = cdesc->id;
920 narg->name = instance_name;
922 vtable = create_vtable (narg, get_network_counter, NULL);
923 vtable->cleanup = network_cleanup;
930 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
932 int id = GPOINTER_TO_INT (vtable->arg);
938 fill_sample (sample);
939 sample->baseValue = 1;
941 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
943 case COUNTER_PROC_USER_TIME:
944 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
946 case COUNTER_PROC_PRIV_TIME:
947 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
949 case COUNTER_PROC_PROC_TIME:
950 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
952 case COUNTER_PROC_THREADS:
953 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
955 case COUNTER_PROC_VBYTES:
956 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
958 case COUNTER_PROC_WSET:
959 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
961 case COUNTER_PROC_PBYTES:
962 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
969 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
971 int id = id_from_string (instance, TRUE) << 5;
972 const CounterDesc *cdesc;
974 /* increase the shift above and the mask also in the implementation functions */
975 //g_assert (32 > desc [1].first_counter - desc->first_counter);
976 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
978 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
984 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
986 int id = GPOINTER_TO_INT (vtable->arg);
988 fill_sample (sample);
989 sample->baseValue = 1;
991 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
993 case COUNTER_MEM_NUM_OBJECTS:
994 sample->rawValue = 0;
996 case COUNTER_MEM_PHYS_TOTAL:
997 sample->rawValue = mono_determine_physical_ram_size ();;
999 case COUNTER_MEM_PHYS_AVAILABLE:
1000 sample->rawValue = mono_determine_physical_ram_available_size ();;
1007 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1009 const CounterDesc *cdesc;
1011 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1012 *type = cdesc->type;
1013 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1019 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1021 PredefVtable *vt = (PredefVtable *)vtable;
1022 const CounterDesc *desc;
1023 int cat_id = GPOINTER_TO_INT (vtable->arg);
1024 int id = cat_id >> 16;
1027 fill_sample (sample);
1028 sample->baseValue = 1;
1030 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1031 sample->counterType = desc->type;
1032 /* FIXME: check that the offset fits inside imported counters */
1033 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1034 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1039 predef_vtable (void *arg, MonoString *instance)
1041 MonoSharedArea *area;
1042 PredefVtable *vtable;
1043 char *pids = mono_string_to_utf8 (instance);
1048 area = load_sarea_for_pid (pid);
1052 vtable = g_new (PredefVtable, 1);
1053 vtable->vtable.arg = arg;
1054 vtable->vtable.sample = predef_readonly_counter;
1055 vtable->vtable.cleanup = predef_cleanup;
1056 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1059 return (ImplVtable*)vtable;
1062 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1063 * this needs some way to set sample->counterType as well, though.
1066 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1068 int cat_id = GPOINTER_TO_INT (vtable->arg);
1069 int id = cat_id >> 16;
1072 fill_sample (sample);
1073 sample->baseValue = 1;
1075 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1079 case COUNTER_EXC_THROWN:
1080 sample->rawValue = mono_perfcounters->exceptions_thrown;
1084 case CATEGORY_ASPNET:
1086 case COUNTER_ASPNET_REQ_Q:
1087 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1089 case COUNTER_ASPNET_REQ_TOTAL:
1090 sample->rawValue = mono_perfcounters->aspnet_requests;
1094 case CATEGORY_THREADPOOL:
1096 case COUNTER_THREADPOOL_WORKITEMS:
1097 sample->rawValue = mono_perfcounters->threadpool_workitems;
1099 case COUNTER_THREADPOOL_IOWORKITEMS:
1100 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1102 case COUNTER_THREADPOOL_THREADS:
1103 sample->rawValue = mono_perfcounters->threadpool_threads;
1105 case COUNTER_THREADPOOL_IOTHREADS:
1106 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1112 case COUNTER_JIT_BYTES:
1113 sample->rawValue = mono_perfcounters->jit_bytes;
1115 case COUNTER_JIT_METHODS:
1116 sample->rawValue = mono_perfcounters->jit_methods;
1118 case COUNTER_JIT_TIME:
1119 sample->rawValue = mono_perfcounters->jit_time;
1121 case COUNTER_JIT_BYTES_PSEC:
1122 sample->rawValue = mono_perfcounters->jit_bytes;
1124 case COUNTER_JIT_FAILURES:
1125 sample->rawValue = mono_perfcounters->jit_failures;
1134 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1136 guint32 *volatile ptr = NULL;
1137 gint64 *volatile ptr64 = NULL;
1138 int cat_id = GPOINTER_TO_INT (vtable->arg);
1139 int id = cat_id >> 16;
1142 case CATEGORY_ASPNET:
1144 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1145 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1148 case CATEGORY_THREADPOOL:
1150 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1151 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1152 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1153 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1160 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1162 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1167 /* this can be non-atomic */
1172 /* FIXME: we need to do this atomically */
1173 /* No InterlockedIncrement64() yet */
1176 return InterlockedIncrement64 (ptr);
1178 return InterlockedDecrement64 (ptr);
1184 /* this can be non-atomic */
1192 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1194 const CounterDesc *cdesc;
1196 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1197 *type = cdesc->type;
1198 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1199 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1201 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1207 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1209 CustomVTable *counter_data = (CustomVTable *)vtable;
1211 fill_sample (sample);
1212 sample->baseValue = 1;
1214 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1216 sample->rawValue = 0;
1218 sample->rawValue = *(guint64*)vtable->arg;
1223 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1225 /* FIXME: check writability */
1226 guint64 *ptr = (guint64 *)vtable->arg;
1229 /* FIXME: we need to do this atomically */
1233 /* this can be non-atomic */
1240 static SharedInstance*
1241 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1243 SharedInstance* inst;
1246 inst = find_custom_instance (cat, name);
1249 size = sizeof (SharedInstance) + strlen (name);
1252 size += (sizeof (guint64) * cat->num_counters);
1254 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1260 inst->category_offset = (char*)cat - (char*)shared_area;
1261 cat->num_instances++;
1262 /* now copy the variable data */
1263 p = inst->instance_name;
1265 p += strlen (name) + 1;
1272 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1274 CustomVTable* vtable;
1275 vtable = g_new0 (CustomVTable, 1);
1276 vtable->vtable.arg = data;
1277 vtable->vtable.sample = custom_writable_counter;
1278 vtable->vtable.update = custom_writable_update;
1279 vtable->instance_desc = inst;
1280 vtable->counter_desc = scounter;
1282 return (ImplVtable*)vtable;
1286 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1288 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1291 offset += scounter->seq_num * sizeof (guint64);
1292 return (char*)sinst + offset;
1296 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1298 SharedCounter *scounter;
1299 SharedInstance* inst;
1302 scounter = find_custom_counter (cat, counter);
1305 *type = simple_type_to_type [scounter->type];
1306 name = mono_string_to_utf8 (counter);
1307 inst = custom_get_instance (cat, scounter, name);
1311 return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1314 static const CategoryDesc*
1315 find_category (MonoString *category)
1318 for (i = 0; i < NUM_CATEGORIES; ++i) {
1319 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1320 return &predef_categories [i];
1326 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1327 MonoString* machine, int *type, MonoBoolean *custom)
1329 const CategoryDesc *cdesc;
1330 /* no support for counters on other machines */
1331 if (mono_string_compare_ascii (machine, "."))
1333 cdesc = find_category (category);
1335 SharedCategory *scat = find_custom_category (category);
1339 return custom_get_impl (scat, counter, instance, type);
1341 switch (cdesc->id) {
1343 return cpu_get_impl (counter, instance, type, custom);
1345 return process_get_impl (counter, instance, type, custom);
1346 case CATEGORY_MONO_MEM:
1347 return mono_mem_get_impl (counter, instance, type, custom);
1348 case CATEGORY_NETWORK:
1349 return network_get_impl (counter, instance, type, custom);
1353 case CATEGORY_REMOTING:
1354 case CATEGORY_LOADING:
1355 case CATEGORY_THREAD:
1356 case CATEGORY_INTEROP:
1357 case CATEGORY_SECURITY:
1358 case CATEGORY_ASPNET:
1359 case CATEGORY_THREADPOOL:
1360 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1366 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1368 ImplVtable *vtable = (ImplVtable *)impl;
1369 if (vtable && vtable->sample)
1370 return vtable->sample (vtable, only_value, sample);
1375 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1377 ImplVtable *vtable = (ImplVtable *)impl;
1378 if (vtable && vtable->update)
1379 return vtable->update (vtable, do_incr, value);
1384 mono_perfcounter_free_data (void *impl)
1386 ImplVtable *vtable = (ImplVtable *)impl;
1387 if (vtable && vtable->cleanup)
1388 vtable->cleanup (vtable);
1392 /* Category icalls */
1394 mono_perfcounter_category_del (MonoString *name)
1396 const CategoryDesc *cdesc;
1397 SharedCategory *cat;
1398 cdesc = find_category (name);
1399 /* can't delete a predefined category */
1403 cat = find_custom_category (name);
1404 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1405 if (!cat || cat->num_instances) {
1409 cat->header.ftype = FTYPE_DELETED;
1415 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1417 const CategoryDesc *cdesc;
1418 /* no support for counters on other machines */
1419 if (mono_string_compare_ascii (machine, "."))
1421 cdesc = find_category (category);
1423 SharedCategory *scat = find_custom_category (category);
1426 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1428 return mono_string_new (mono_domain_get (), cdesc->help);
1432 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1433 * TRUE only if a counter with that name exists in the category.
1436 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1438 const CategoryDesc *cdesc;
1439 /* no support for counters on other machines */
1440 if (mono_string_compare_ascii (machine, "."))
1442 cdesc = find_category (category);
1444 SharedCategory *scat = find_custom_category (category);
1447 /* counter is allowed to be null */
1450 /* search through the custom category */
1451 return find_custom_counter (scat, counter) != NULL;
1453 /* counter is allowed to be null */
1456 if (get_counter_in_category (cdesc, counter))
1461 /* C map of the type with the same name */
1467 } CounterCreationData;
1470 * Since we'll keep a copy of the category per-process, we should also make sure
1471 * categories with the same name are compatible.
1474 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1479 int num_counters = mono_array_length (items);
1480 int counters_data_size;
1483 char **counter_info = NULL;
1485 SharedCategory *cat;
1487 /* FIXME: ensure there isn't a category created already */
1488 mono_error_init (&error);
1489 name = mono_string_to_utf8_checked (category, &error);
1490 if (!mono_error_ok (&error))
1492 chelp = mono_string_to_utf8_checked (help, &error);
1493 if (!mono_error_ok (&error))
1495 counter_info = g_new0 (char*, num_counters * 2);
1496 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1497 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1498 for (i = 0; i < num_counters; ++i) {
1499 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1500 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1501 if (!mono_error_ok (&error))
1503 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1504 if (!mono_error_ok (&error))
1506 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1508 for (i = 0; i < num_counters * 2; ++i) {
1509 if (!counter_info [i])
1511 size += strlen (counter_info [i]) + 1;
1515 counters_data_size = num_counters * 8; /* optimize for size later */
1519 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1524 cat->num_counters = num_counters;
1525 cat->counters_data_size = counters_data_size;
1526 /* now copy the vaiable data */
1529 p += strlen (name) + 1;
1531 p += strlen (chelp) + 1;
1532 for (i = 0; i < num_counters; ++i) {
1533 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1534 /* emit the SharedCounter structures */
1535 *p++ = perfctr_type_compress (data->type);
1537 strcpy (p, counter_info [i * 2]);
1538 p += strlen (counter_info [i * 2]) + 1;
1539 strcpy (p, counter_info [i * 2 + 1]);
1540 p += strlen (counter_info [i * 2 + 1]) + 1;
1547 for (i = 0; i < num_counters * 2; ++i) {
1548 g_free (counter_info [i]);
1550 g_free (counter_info);
1554 mono_error_cleanup (&error);
1559 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1561 const CategoryDesc *cdesc;
1562 SharedInstance *sinst;
1564 /* no support for counters on other machines */
1565 /*FIXME: machine appears to be wrong
1566 if (mono_string_compare_ascii (machine, "."))
1568 cdesc = find_category (category);
1570 SharedCategory *scat;
1571 scat = find_custom_category (category);
1574 name = mono_string_to_utf8 (instance);
1575 sinst = find_custom_instance (scat, name);
1580 /* FIXME: search instance */
1586 mono_perfcounter_category_names (MonoString *machine)
1590 MonoDomain *domain = mono_domain_get ();
1591 GSList *custom_categories, *tmp;
1592 /* no support for counters on other machines */
1593 if (mono_string_compare_ascii (machine, "."))
1594 return mono_array_new (domain, mono_get_string_class (), 0);
1596 custom_categories = get_custom_categories ();
1597 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1598 for (i = 0; i < NUM_CATEGORIES; ++i) {
1599 const CategoryDesc *cdesc = &predef_categories [i];
1600 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1602 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1603 SharedCategory *scat = (SharedCategory *)tmp->data;
1604 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1608 g_slist_free (custom_categories);
1613 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1616 SharedCategory *scat;
1617 const CategoryDesc *cdesc;
1619 MonoDomain *domain = mono_domain_get ();
1620 /* no support for counters on other machines */
1621 if (mono_string_compare_ascii (machine, "."))
1622 return mono_array_new (domain, mono_get_string_class (), 0);
1623 cdesc = find_category (category);
1625 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1626 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1627 const CounterDesc *desc = &predef_counters [i];
1628 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1633 scat = find_custom_category (category);
1635 char *p = custom_category_counters (scat);
1637 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1638 for (i = 0; i < scat->num_counters; ++i) {
1639 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1640 p += 2; /* skip counter type */
1641 p += strlen (p) + 1; /* skip counter name */
1642 p += strlen (p) + 1; /* skip counter help */
1648 return mono_array_new (domain, mono_get_string_class (), 0);
1652 get_string_array (void **array, int count, gboolean is_process)
1655 MonoDomain *domain = mono_domain_get ();
1656 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1657 for (i = 0; i < count; ++i) {
1661 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1662 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1664 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1667 mono_array_setref (res, i, mono_string_new (domain, p));
1675 get_string_array_of_strings (void **array, int count)
1678 MonoDomain *domain = mono_domain_get ();
1679 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1680 for (i = 0; i < count; ++i) {
1681 char* p = (char *)array[i];
1682 mono_array_setref (res, i, mono_string_new (domain, p));
1689 get_mono_instances (void)
1698 buf = g_new (void*, count);
1699 res = mono_shared_area_instances (buf, count);
1700 } while (res == count);
1701 array = get_string_array (buf, res, TRUE);
1707 get_cpu_instances (void)
1713 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1714 buf = g_new (void*, count);
1715 for (i = 0; i < count; ++i)
1716 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1717 array = get_string_array (buf, count, FALSE);
1719 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1724 get_processes_instances (void)
1728 void **buf = mono_process_list (&count);
1730 return get_string_array (NULL, 0, FALSE);
1731 array = get_string_array (buf, count, TRUE);
1737 get_networkinterface_instances (void)
1741 void **buf = mono_networkinterface_list (&count);
1743 return get_string_array_of_strings (NULL, 0);
1744 array = get_string_array_of_strings (buf, count);
1745 g_strfreev ((char **) buf);
1750 get_custom_instances (MonoString *category)
1752 SharedCategory *scat;
1753 scat = find_custom_category (category);
1755 GSList *list = get_custom_instances_list (scat);
1758 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1759 for (tmp = list; tmp; tmp = tmp->next) {
1760 SharedInstance *inst = (SharedInstance *)tmp->data;
1761 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1764 g_slist_free (list);
1767 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1771 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1773 const CategoryDesc* cat;
1774 if (mono_string_compare_ascii (machine, "."))
1775 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1776 cat = find_category (category);
1778 return get_custom_instances (category);
1779 switch (cat->instance_type) {
1781 return get_mono_instances ();
1783 return get_cpu_instances ();
1784 case ProcessInstance:
1785 return get_processes_instances ();
1786 case NetworkInterfaceInstance:
1787 return get_networkinterface_instances ();
1788 case ThreadInstance:
1790 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1795 PerfCounterEnumCallback cb;
1797 } PerfCounterForeachData;
1800 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1806 SharedCategory *cat;
1807 SharedCounter *counter;
1808 SharedInstance *inst;
1809 PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1811 if (header->ftype == FTYPE_CATEGORY) {
1812 cat = (SharedCategory*)header;
1815 p += strlen (p) + 1; /* skip category name */
1816 p += strlen (p) + 1; /* skip category help */
1818 for (i = 0; i < cat->num_counters; ++i) {
1819 counter = (SharedCounter*) p;
1820 type = (unsigned char)*p++;
1821 /* seq_num = (int)* */ p++;
1823 p += strlen (p) + 1;
1825 p += strlen (p) + 1;
1827 inst = custom_get_instance (cat, counter, name);
1830 addr = custom_get_value_address (counter, inst);
1831 if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1840 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1842 PerfCounterForeachData foreach_data = { cb, data };
1846 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1853 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1855 g_assert_not_reached ();
1859 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1861 g_assert_not_reached ();
1865 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1867 g_assert_not_reached ();
1871 mono_perfcounter_free_data (void *impl)
1873 g_assert_not_reached ();
1876 /* Category icalls */
1878 mono_perfcounter_category_del (MonoString *name)
1880 g_assert_not_reached ();
1884 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1886 g_assert_not_reached ();
1890 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1892 g_assert_not_reached ();
1896 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1898 g_assert_not_reached ();
1902 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1904 g_assert_not_reached ();
1908 mono_perfcounter_category_names (MonoString *machine)
1910 g_assert_not_reached ();
1914 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1916 g_assert_not_reached ();
1920 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1922 g_assert_not_reached ();