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;
507 ret = host_statistics(host, HOST_VM_INFO, (host_info_t)&vmstat, &count);
508 } while (ret == KERN_ABORTED);
510 if (ret != KERN_SUCCESS) {
511 g_warning ("Mono was unable to retrieve memory usage!");
515 host_page_size(host, &page_size);
516 return (guint64) vmstat.free_count * page_size;
518 #elif defined (HAVE_SYSCONF)
519 guint64 page_size = 0, num_pages = 0;
521 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
522 * reports invalid values, please add your OS specific code below. */
524 page_size = (guint64)sysconf (_SC_PAGESIZE);
527 #ifdef _SC_AVPHYS_PAGES
528 num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
531 if (!page_size || !num_pages) {
532 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
536 return page_size * num_pages;
543 mono_perfcounters_init (void)
545 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
549 mono_os_mutex_init_recursive (&perfctr_mutex);
551 shared_area = (MonoSharedArea *)mono_shared_area ();
552 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
553 shared_area->counters_size = sizeof (MonoPerfCounters);
554 shared_area->data_start = d_offset;
555 shared_area->size = 4096;
556 mono_perfcounters = &shared_area->counters;
560 perfctr_type_compress (int type)
563 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
564 if (simple_type_to_type [i] == type)
567 /* NumberOfItems32 */
572 shared_data_reserve_room (int size, int ftype)
574 SharedHeader* header;
575 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
576 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
581 unsigned short *next;
582 if (*p == FTYPE_END) {
583 if (size < (end - p))
589 next = (unsigned short*)(p + 2);
590 if (*p == FTYPE_DELETED) {
591 /* we reuse only if it's the same size */
601 header = (SharedHeader*)p;
602 header->ftype = ftype;
603 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
609 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
612 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
615 unsigned short *next;
618 next = (unsigned short*)(p + 2);
619 if (!func ((SharedHeader*)p, data))
628 foreach_shared_item (SharedFunc func, void *data)
630 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
631 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
633 foreach_shared_item_in_area (p, end, func, data);
637 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
639 /* FIXME: make this case insensitive */
640 guint16 *strc = mono_string_chars (str);
641 while (*strc == *ascii_str++) {
646 return *strc - *(const unsigned char *)(ascii_str - 1);
655 category_search (SharedHeader *header, void *data)
657 CatSearch *search = (CatSearch *)data;
658 if (header->ftype == FTYPE_CATEGORY) {
659 SharedCategory *cat = (SharedCategory*)header;
660 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
668 static SharedCategory*
669 find_custom_category (MonoString *name)
674 foreach_shared_item (category_search, &search);
679 category_collect (SharedHeader *header, void *data)
681 GSList **list = (GSList **)data;
682 if (header->ftype == FTYPE_CATEGORY) {
683 *list = g_slist_prepend (*list, header);
689 get_custom_categories (void) {
691 foreach_shared_item (category_collect, &list);
696 custom_category_counters (SharedCategory* cat)
698 char *p = cat->name + strlen (cat->name) + 1;
699 p += strlen (p) + 1; /* skip category help */
703 static SharedCounter*
704 find_custom_counter (SharedCategory* cat, MonoString *name)
707 char *p = custom_category_counters (cat);
708 for (i = 0; i < cat->num_counters; ++i) {
709 SharedCounter *counter = (SharedCounter*)p;
710 if (mono_string_compare_ascii (name, counter->name) == 0)
712 p += 2; /* skip counter type */
713 p += strlen (p) + 1; /* skip counter name */
714 p += strlen (p) + 1; /* skip counter help */
720 unsigned int cat_offset;
723 SharedInstance* result;
728 instance_search (SharedHeader *header, void *data)
730 InstanceSearch *search = (InstanceSearch *)data;
731 if (header->ftype == FTYPE_INSTANCE) {
732 SharedInstance *ins = (SharedInstance*)header;
733 if (search->cat_offset == ins->category_offset) {
735 if (strcmp (search->name, ins->instance_name) == 0) {
736 search->result = ins;
740 search->list = g_slist_prepend (search->list, ins);
747 static SharedInstance*
748 find_custom_instance (SharedCategory* cat, char *name)
750 InstanceSearch search;
751 search.cat_offset = (char*)cat - (char*)shared_area;
755 search.result = NULL;
756 foreach_shared_item (instance_search, &search);
757 return search.result;
761 get_custom_instances_list (SharedCategory* cat)
763 InstanceSearch search;
764 search.cat_offset = (char*)cat - (char*)shared_area;
768 search.result = NULL;
769 foreach_shared_item (instance_search, &search);
774 custom_category_help (SharedCategory* cat)
776 return cat->name + strlen (cat->name) + 1;
779 static const CounterDesc*
780 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
782 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
783 const CounterDesc *end = &predef_counters [desc [1].first_counter];
784 for (; cdesc < end; ++cdesc) {
785 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
791 /* fill the info in sample (except the raw value) */
793 fill_sample (MonoCounterSample *sample)
795 sample->timeStamp = mono_100ns_ticks ();
796 sample->timeStamp100nSec = sample->timeStamp;
797 sample->counterTimeStamp = sample->timeStamp;
798 sample->counterFrequency = 10000000;
799 sample->systemFrequency = 10000000;
800 // the real basevalue needs to be get from a different counter...
801 sample->baseValue = 0;
805 id_from_string (MonoString *instance, gboolean is_process)
808 if (mono_string_length (instance)) {
809 char *id_str = mono_string_to_utf8 (instance);
811 id = strtol (id_str, &end, 0);
812 if (end == id_str && !is_process)
820 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
822 MonoProcessError error;
823 int id = GPOINTER_TO_INT (vtable->arg);
827 fill_sample (sample);
828 sample->baseValue = 1;
830 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
832 case COUNTER_CPU_USER_TIME:
833 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
835 case COUNTER_CPU_PRIV_TIME:
836 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
838 case COUNTER_CPU_INTR_TIME:
839 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
841 case COUNTER_CPU_DCP_TIME:
842 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
844 case COUNTER_CPU_PROC_TIME:
845 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
852 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
854 int id = id_from_string (instance, FALSE) << 5;
855 const CounterDesc *cdesc;
857 /* increase the shift above and the mask also in the implementation functions */
858 //g_assert (32 > desc [1].first_counter - desc->first_counter);
859 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
861 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
867 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
869 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
870 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
872 fill_sample (sample);
875 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
877 case COUNTER_NETWORK_BYTESRECSEC:
878 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
880 case COUNTER_NETWORK_BYTESSENTSEC:
881 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
883 case COUNTER_NETWORK_BYTESTOTALSEC:
884 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
888 if (error == MONO_NETWORK_ERROR_NONE)
895 network_cleanup (ImplVtable *vtable)
897 NetworkVtableArg *narg;
902 narg = (NetworkVtableArg *)vtable->arg;
913 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
915 const CounterDesc *cdesc;
916 NetworkVtableArg *narg;
921 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
922 instance_name = mono_string_to_utf8 (instance);
923 narg = g_new0 (NetworkVtableArg, 1);
924 narg->id = cdesc->id;
925 narg->name = instance_name;
927 vtable = create_vtable (narg, get_network_counter, NULL);
928 vtable->cleanup = network_cleanup;
935 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
937 int id = GPOINTER_TO_INT (vtable->arg);
943 fill_sample (sample);
944 sample->baseValue = 1;
946 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
948 case COUNTER_PROC_USER_TIME:
949 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
951 case COUNTER_PROC_PRIV_TIME:
952 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
954 case COUNTER_PROC_PROC_TIME:
955 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
957 case COUNTER_PROC_THREADS:
958 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
960 case COUNTER_PROC_VBYTES:
961 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
963 case COUNTER_PROC_WSET:
964 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
966 case COUNTER_PROC_PBYTES:
967 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
974 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
976 int id = id_from_string (instance, TRUE) << 5;
977 const CounterDesc *cdesc;
979 /* increase the shift above and the mask also in the implementation functions */
980 //g_assert (32 > desc [1].first_counter - desc->first_counter);
981 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
983 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
989 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
991 int id = GPOINTER_TO_INT (vtable->arg);
993 fill_sample (sample);
994 sample->baseValue = 1;
996 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
998 case COUNTER_MEM_NUM_OBJECTS:
999 sample->rawValue = 0;
1001 case COUNTER_MEM_PHYS_TOTAL:
1002 sample->rawValue = mono_determine_physical_ram_size ();;
1004 case COUNTER_MEM_PHYS_AVAILABLE:
1005 sample->rawValue = mono_determine_physical_ram_available_size ();;
1012 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1014 const CounterDesc *cdesc;
1016 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
1017 *type = cdesc->type;
1018 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
1024 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1026 PredefVtable *vt = (PredefVtable *)vtable;
1027 const CounterDesc *desc;
1028 int cat_id = GPOINTER_TO_INT (vtable->arg);
1029 int id = cat_id >> 16;
1032 fill_sample (sample);
1033 sample->baseValue = 1;
1035 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
1036 sample->counterType = desc->type;
1037 /* FIXME: check that the offset fits inside imported counters */
1038 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1039 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
1044 predef_vtable (void *arg, MonoString *instance)
1046 MonoSharedArea *area;
1047 PredefVtable *vtable;
1048 char *pids = mono_string_to_utf8 (instance);
1053 area = load_sarea_for_pid (pid);
1057 vtable = g_new (PredefVtable, 1);
1058 vtable->vtable.arg = arg;
1059 vtable->vtable.sample = predef_readonly_counter;
1060 vtable->vtable.cleanup = predef_cleanup;
1061 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
1064 return (ImplVtable*)vtable;
1067 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1068 * this needs some way to set sample->counterType as well, though.
1071 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1073 int cat_id = GPOINTER_TO_INT (vtable->arg);
1074 int id = cat_id >> 16;
1077 fill_sample (sample);
1078 sample->baseValue = 1;
1080 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1084 case COUNTER_EXC_THROWN:
1085 sample->rawValue = mono_perfcounters->exceptions_thrown;
1089 case CATEGORY_ASPNET:
1091 case COUNTER_ASPNET_REQ_Q:
1092 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
1094 case COUNTER_ASPNET_REQ_TOTAL:
1095 sample->rawValue = mono_perfcounters->aspnet_requests;
1099 case CATEGORY_THREADPOOL:
1101 case COUNTER_THREADPOOL_WORKITEMS:
1102 sample->rawValue = mono_perfcounters->threadpool_workitems;
1104 case COUNTER_THREADPOOL_IOWORKITEMS:
1105 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
1107 case COUNTER_THREADPOOL_THREADS:
1108 sample->rawValue = mono_perfcounters->threadpool_threads;
1110 case COUNTER_THREADPOOL_IOTHREADS:
1111 sample->rawValue = mono_perfcounters->threadpool_iothreads;
1117 case COUNTER_JIT_BYTES:
1118 sample->rawValue = mono_perfcounters->jit_bytes;
1120 case COUNTER_JIT_METHODS:
1121 sample->rawValue = mono_perfcounters->jit_methods;
1123 case COUNTER_JIT_TIME:
1124 sample->rawValue = mono_perfcounters->jit_time;
1126 case COUNTER_JIT_BYTES_PSEC:
1127 sample->rawValue = mono_perfcounters->jit_bytes;
1129 case COUNTER_JIT_FAILURES:
1130 sample->rawValue = mono_perfcounters->jit_failures;
1139 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1141 guint32 *volatile ptr = NULL;
1142 gint64 *volatile ptr64 = NULL;
1143 int cat_id = GPOINTER_TO_INT (vtable->arg);
1144 int id = cat_id >> 16;
1147 case CATEGORY_ASPNET:
1149 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
1150 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
1153 case CATEGORY_THREADPOOL:
1155 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
1156 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
1157 case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
1158 case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
1165 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
1167 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
1172 /* this can be non-atomic */
1177 /* FIXME: we need to do this atomically */
1178 /* No InterlockedIncrement64() yet */
1181 return InterlockedIncrement64 (ptr);
1183 return InterlockedDecrement64 (ptr);
1189 /* this can be non-atomic */
1197 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
1199 const CounterDesc *cdesc;
1201 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
1202 *type = cdesc->type;
1203 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
1204 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
1206 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1212 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1214 CustomVTable *counter_data = (CustomVTable *)vtable;
1216 fill_sample (sample);
1217 sample->baseValue = 1;
1219 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1221 sample->rawValue = 0;
1223 sample->rawValue = *(guint64*)vtable->arg;
1228 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1230 /* FIXME: check writability */
1231 guint64 *ptr = (guint64 *)vtable->arg;
1234 /* FIXME: we need to do this atomically */
1238 /* this can be non-atomic */
1245 static SharedInstance*
1246 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1248 SharedInstance* inst;
1251 inst = find_custom_instance (cat, name);
1254 size = sizeof (SharedInstance) + strlen (name);
1257 size += (sizeof (guint64) * cat->num_counters);
1259 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1265 inst->category_offset = (char*)cat - (char*)shared_area;
1266 cat->num_instances++;
1267 /* now copy the variable data */
1268 p = inst->instance_name;
1270 p += strlen (name) + 1;
1277 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1279 CustomVTable* vtable;
1280 vtable = g_new0 (CustomVTable, 1);
1281 vtable->vtable.arg = data;
1282 vtable->vtable.sample = custom_writable_counter;
1283 vtable->vtable.update = custom_writable_update;
1284 vtable->instance_desc = inst;
1285 vtable->counter_desc = scounter;
1287 return (ImplVtable*)vtable;
1291 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1293 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1296 offset += scounter->seq_num * sizeof (guint64);
1297 return (char*)sinst + offset;
1301 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1303 SharedCounter *scounter;
1304 SharedInstance* inst;
1307 scounter = find_custom_counter (cat, counter);
1310 *type = simple_type_to_type [scounter->type];
1311 name = mono_string_to_utf8 (counter);
1312 inst = custom_get_instance (cat, scounter, name);
1316 return custom_vtable (scounter, inst, (char *)custom_get_value_address (scounter, inst));
1319 static const CategoryDesc*
1320 find_category (MonoString *category)
1323 for (i = 0; i < NUM_CATEGORIES; ++i) {
1324 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1325 return &predef_categories [i];
1331 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1332 MonoString* machine, int *type, MonoBoolean *custom)
1334 const CategoryDesc *cdesc;
1335 /* no support for counters on other machines */
1336 if (mono_string_compare_ascii (machine, "."))
1338 cdesc = find_category (category);
1340 SharedCategory *scat = find_custom_category (category);
1344 return custom_get_impl (scat, counter, instance, type);
1346 switch (cdesc->id) {
1348 return cpu_get_impl (counter, instance, type, custom);
1350 return process_get_impl (counter, instance, type, custom);
1351 case CATEGORY_MONO_MEM:
1352 return mono_mem_get_impl (counter, instance, type, custom);
1353 case CATEGORY_NETWORK:
1354 return network_get_impl (counter, instance, type, custom);
1358 case CATEGORY_REMOTING:
1359 case CATEGORY_LOADING:
1360 case CATEGORY_THREAD:
1361 case CATEGORY_INTEROP:
1362 case CATEGORY_SECURITY:
1363 case CATEGORY_ASPNET:
1364 case CATEGORY_THREADPOOL:
1365 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1371 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1373 ImplVtable *vtable = (ImplVtable *)impl;
1374 if (vtable && vtable->sample)
1375 return vtable->sample (vtable, only_value, sample);
1380 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1382 ImplVtable *vtable = (ImplVtable *)impl;
1383 if (vtable && vtable->update)
1384 return vtable->update (vtable, do_incr, value);
1389 mono_perfcounter_free_data (void *impl)
1391 ImplVtable *vtable = (ImplVtable *)impl;
1392 if (vtable && vtable->cleanup)
1393 vtable->cleanup (vtable);
1397 /* Category icalls */
1399 mono_perfcounter_category_del (MonoString *name)
1401 const CategoryDesc *cdesc;
1402 SharedCategory *cat;
1403 cdesc = find_category (name);
1404 /* can't delete a predefined category */
1408 cat = find_custom_category (name);
1409 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1410 if (!cat || cat->num_instances) {
1414 cat->header.ftype = FTYPE_DELETED;
1420 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1422 const CategoryDesc *cdesc;
1423 /* no support for counters on other machines */
1424 if (mono_string_compare_ascii (machine, "."))
1426 cdesc = find_category (category);
1428 SharedCategory *scat = find_custom_category (category);
1431 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1433 return mono_string_new (mono_domain_get (), cdesc->help);
1437 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1438 * TRUE only if a counter with that name exists in the category.
1441 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1443 const CategoryDesc *cdesc;
1444 /* no support for counters on other machines */
1445 if (mono_string_compare_ascii (machine, "."))
1447 cdesc = find_category (category);
1449 SharedCategory *scat = find_custom_category (category);
1452 /* counter is allowed to be null */
1455 /* search through the custom category */
1456 return find_custom_counter (scat, counter) != NULL;
1458 /* counter is allowed to be null */
1461 if (get_counter_in_category (cdesc, counter))
1466 /* C map of the type with the same name */
1472 } CounterCreationData;
1475 * Since we'll keep a copy of the category per-process, we should also make sure
1476 * categories with the same name are compatible.
1479 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1484 int num_counters = mono_array_length (items);
1485 int counters_data_size;
1488 char **counter_info = NULL;
1490 SharedCategory *cat;
1492 /* FIXME: ensure there isn't a category created already */
1493 mono_error_init (&error);
1494 name = mono_string_to_utf8_checked (category, &error);
1495 if (!mono_error_ok (&error))
1497 chelp = mono_string_to_utf8_checked (help, &error);
1498 if (!mono_error_ok (&error))
1500 counter_info = g_new0 (char*, num_counters * 2);
1501 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1502 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1503 for (i = 0; i < num_counters; ++i) {
1504 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1505 counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
1506 if (!mono_error_ok (&error))
1508 counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
1509 if (!mono_error_ok (&error))
1511 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1513 for (i = 0; i < num_counters * 2; ++i) {
1514 if (!counter_info [i])
1516 size += strlen (counter_info [i]) + 1;
1520 counters_data_size = num_counters * 8; /* optimize for size later */
1524 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1529 cat->num_counters = num_counters;
1530 cat->counters_data_size = counters_data_size;
1531 /* now copy the vaiable data */
1534 p += strlen (name) + 1;
1536 p += strlen (chelp) + 1;
1537 for (i = 0; i < num_counters; ++i) {
1538 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1539 /* emit the SharedCounter structures */
1540 *p++ = perfctr_type_compress (data->type);
1542 strcpy (p, counter_info [i * 2]);
1543 p += strlen (counter_info [i * 2]) + 1;
1544 strcpy (p, counter_info [i * 2 + 1]);
1545 p += strlen (counter_info [i * 2 + 1]) + 1;
1552 for (i = 0; i < num_counters * 2; ++i) {
1553 g_free (counter_info [i]);
1555 g_free (counter_info);
1559 mono_error_cleanup (&error);
1564 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1566 const CategoryDesc *cdesc;
1567 SharedInstance *sinst;
1569 /* no support for counters on other machines */
1570 /*FIXME: machine appears to be wrong
1571 if (mono_string_compare_ascii (machine, "."))
1573 cdesc = find_category (category);
1575 SharedCategory *scat;
1576 scat = find_custom_category (category);
1579 name = mono_string_to_utf8 (instance);
1580 sinst = find_custom_instance (scat, name);
1585 /* FIXME: search instance */
1591 mono_perfcounter_category_names (MonoString *machine)
1595 MonoDomain *domain = mono_domain_get ();
1596 GSList *custom_categories, *tmp;
1597 /* no support for counters on other machines */
1598 if (mono_string_compare_ascii (machine, "."))
1599 return mono_array_new (domain, mono_get_string_class (), 0);
1601 custom_categories = get_custom_categories ();
1602 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1603 for (i = 0; i < NUM_CATEGORIES; ++i) {
1604 const CategoryDesc *cdesc = &predef_categories [i];
1605 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1607 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1608 SharedCategory *scat = (SharedCategory *)tmp->data;
1609 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1613 g_slist_free (custom_categories);
1618 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1621 SharedCategory *scat;
1622 const CategoryDesc *cdesc;
1624 MonoDomain *domain = mono_domain_get ();
1625 /* no support for counters on other machines */
1626 if (mono_string_compare_ascii (machine, "."))
1627 return mono_array_new (domain, mono_get_string_class (), 0);
1628 cdesc = find_category (category);
1630 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1631 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1632 const CounterDesc *desc = &predef_counters [i];
1633 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1638 scat = find_custom_category (category);
1640 char *p = custom_category_counters (scat);
1642 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1643 for (i = 0; i < scat->num_counters; ++i) {
1644 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1645 p += 2; /* skip counter type */
1646 p += strlen (p) + 1; /* skip counter name */
1647 p += strlen (p) + 1; /* skip counter help */
1653 return mono_array_new (domain, mono_get_string_class (), 0);
1657 get_string_array (void **array, int count, gboolean is_process)
1660 MonoDomain *domain = mono_domain_get ();
1661 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1662 for (i = 0; i < count; ++i) {
1666 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1667 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1669 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1672 mono_array_setref (res, i, mono_string_new (domain, p));
1680 get_string_array_of_strings (void **array, int count)
1683 MonoDomain *domain = mono_domain_get ();
1684 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1685 for (i = 0; i < count; ++i) {
1686 char* p = (char *)array[i];
1687 mono_array_setref (res, i, mono_string_new (domain, p));
1694 get_mono_instances (void)
1703 buf = g_new (void*, count);
1704 res = mono_shared_area_instances (buf, count);
1705 } while (res == count);
1706 array = get_string_array (buf, res, TRUE);
1712 get_cpu_instances (void)
1718 count = mono_cpu_count () + 1; /* +1 for "_Total" */
1719 buf = g_new (void*, count);
1720 for (i = 0; i < count; ++i)
1721 buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
1722 array = get_string_array (buf, count, FALSE);
1724 mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
1729 get_processes_instances (void)
1733 void **buf = mono_process_list (&count);
1735 return get_string_array (NULL, 0, FALSE);
1736 array = get_string_array (buf, count, TRUE);
1742 get_networkinterface_instances (void)
1746 void **buf = mono_networkinterface_list (&count);
1748 return get_string_array_of_strings (NULL, 0);
1749 array = get_string_array_of_strings (buf, count);
1750 g_strfreev ((char **) buf);
1755 get_custom_instances (MonoString *category)
1757 SharedCategory *scat;
1758 scat = find_custom_category (category);
1760 GSList *list = get_custom_instances_list (scat);
1763 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1764 for (tmp = list; tmp; tmp = tmp->next) {
1765 SharedInstance *inst = (SharedInstance *)tmp->data;
1766 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1769 g_slist_free (list);
1772 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1776 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1778 const CategoryDesc* cat;
1779 if (mono_string_compare_ascii (machine, "."))
1780 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1781 cat = find_category (category);
1783 return get_custom_instances (category);
1784 switch (cat->instance_type) {
1786 return get_mono_instances ();
1788 return get_cpu_instances ();
1789 case ProcessInstance:
1790 return get_processes_instances ();
1791 case NetworkInterfaceInstance:
1792 return get_networkinterface_instances ();
1793 case ThreadInstance:
1795 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1800 PerfCounterEnumCallback cb;
1802 } PerfCounterForeachData;
1805 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1811 SharedCategory *cat;
1812 SharedCounter *counter;
1813 SharedInstance *inst;
1814 PerfCounterForeachData *foreach_data = (PerfCounterForeachData *)data;
1816 if (header->ftype == FTYPE_CATEGORY) {
1817 cat = (SharedCategory*)header;
1820 p += strlen (p) + 1; /* skip category name */
1821 p += strlen (p) + 1; /* skip category help */
1823 for (i = 0; i < cat->num_counters; ++i) {
1824 counter = (SharedCounter*) p;
1825 type = (unsigned char)*p++;
1826 /* seq_num = (int)* */ p++;
1828 p += strlen (p) + 1;
1830 p += strlen (p) + 1;
1832 inst = custom_get_instance (cat, counter, name);
1835 addr = custom_get_value_address (counter, inst);
1836 if (!foreach_data->cb (cat->name, name, type, addr ? *(gint64*)addr : 0, foreach_data->data))
1845 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1847 PerfCounterForeachData foreach_data = { cb, data };
1851 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1858 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance, MonoString* machine, int *type, MonoBoolean *custom)
1860 g_assert_not_reached ();
1864 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1866 g_assert_not_reached ();
1870 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1872 g_assert_not_reached ();
1876 mono_perfcounter_free_data (void *impl)
1878 g_assert_not_reached ();
1881 /* Category icalls */
1883 mono_perfcounter_category_del (MonoString *name)
1885 g_assert_not_reached ();
1889 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1891 g_assert_not_reached ();
1895 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1897 g_assert_not_reached ();
1901 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1903 g_assert_not_reached ();
1907 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1909 g_assert_not_reached ();
1913 mono_perfcounter_category_names (MonoString *machine)
1915 g_assert_not_reached ();
1919 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1921 g_assert_not_reached ();
1925 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1927 g_assert_not_reached ();