4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
15 #ifdef HAVE_SYS_TIME_H
18 #include "metadata/mono-perfcounters.h"
19 #include "metadata/appdomain.h"
21 #include "metadata/class-internals.h"
22 #include "utils/mono-time.h"
23 #include "utils/mono-mmap.h"
24 #include "utils/mono-proclib.h"
25 #include "utils/mono-networkinterfaces.h"
26 #include <mono/io-layer/io-layer.h>
28 /* map of CounterSample.cs */
29 struct _MonoCounterSample {
32 gint64 counterFrequency;
33 gint64 systemFrequency;
35 gint64 timeStamp100nSec;
36 gint64 counterTimeStamp;
40 /* map of PerformanceCounterType.cs */
42 NumberOfItemsHEX32=0x00000000,
43 NumberOfItemsHEX64=0x00000100,
44 NumberOfItems32=0x00010000,
45 NumberOfItems64=0x00010100,
46 CounterDelta32=0x00400400,
47 CounterDelta64=0x00400500,
48 SampleCounter=0x00410400,
49 CountPerTimeInterval32=0x00450400,
50 CountPerTimeInterval64=0x00450500,
51 RateOfCountsPerSecond32=0x10410400,
52 RateOfCountsPerSecond64=0x10410500,
53 RawFraction=0x20020400,
54 CounterTimer=0x20410500,
55 Timer100Ns=0x20510500,
56 SampleFraction=0x20C20400,
57 CounterTimerInverse=0x21410500,
58 Timer100NsInverse=0x21510500,
59 CounterMultiTimer=0x22410500,
60 CounterMultiTimer100Ns=0x22510500,
61 CounterMultiTimerInverse=0x23410500,
62 CounterMultiTimer100NsInverse=0x23510500,
63 AverageTimer32=0x30020400,
64 ElapsedTime=0x30240500,
65 AverageCount64=0x40020500,
66 SampleBase=0x40030401,
67 AverageBase=0x40030402,
69 CounterMultiBase=0x42030500
72 /* maps a small integer type to the counter types above */
74 simple_type_to_type [] = {
75 NumberOfItemsHEX32, NumberOfItemsHEX64,
76 NumberOfItems32, NumberOfItems64,
77 CounterDelta32, CounterDelta64,
78 SampleCounter, CountPerTimeInterval32,
79 CountPerTimeInterval64, RateOfCountsPerSecond32,
80 RateOfCountsPerSecond64, RawFraction,
81 CounterTimer, Timer100Ns,
82 SampleFraction, CounterTimerInverse,
83 Timer100NsInverse, CounterMultiTimer,
84 CounterMultiTimer100Ns, CounterMultiTimerInverse,
85 CounterMultiTimer100NsInverse, AverageTimer32,
86 ElapsedTime, AverageCount64,
87 SampleBase, AverageBase,
88 RawBase, CounterMultiBase
102 NetworkInterfaceInstance,
106 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
107 #define PERFCTR_COUNTER(id,name,help,type,field)
109 #include "mono-perfcounters-def.h"
114 #undef PERFCTR_COUNTER
115 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
116 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
117 /* each counter is assigned an id starting from 0 inside the category */
119 #include "mono-perfcounters-def.h"
124 #undef PERFCTR_COUNTER
125 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
126 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
127 /* this is used just to count the number of counters */
129 #include "mono-perfcounters-def.h"
133 static CRITICAL_SECTION perfctr_mutex;
134 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
135 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
140 unsigned short counters_start;
141 unsigned short counters_size;
142 unsigned short data_start;
143 MonoPerfCounters counters;
148 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
150 struct stanza_header {
151 byte stanza_type; // FTYPE_*
153 ushort stanza_length; // includeas header
158 // perfcat and perfinstance are 4-bytes aligned
162 ushort length; // includes the counters
164 ushort counters_data_size;
166 char name[]; // null terminated
167 char help[]; // null terminated
168 // perfcounters follow
171 char name[]; // null terminated
172 char help[]; // null terminated
177 struct perfinstance {
179 byte data_offset; // offset of counters from beginning of struct
181 uint category_offset; // offset of category in the shared area
182 char name[]; // null terminated
183 // data follows: this is always 8-byte aligned
189 FTYPE_CATEGORY = 'C',
191 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
192 FTYPE_INSTANCE = 'I',
205 unsigned short num_counters;
206 unsigned short counters_data_size;
208 /* variable length data follows */
214 unsigned int category_offset;
215 /* variable length data follows */
216 char instance_name [1];
222 /* variable length data follows */
231 unsigned int instance_type : 6;
239 unsigned short offset; // offset inside MonoPerfCounters
244 #undef PERFCTR_COUNTER
245 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
246 #define PERFCTR_COUNTER(id,name,help,type,field)
247 static const CategoryDesc
248 predef_categories [] = {
249 #include "mono-perfcounters-def.h"
250 {NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS}
254 #undef PERFCTR_COUNTER
255 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
256 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
257 static const CounterDesc
258 predef_counters [] = {
259 #include "mono-perfcounters-def.h"
260 {NULL, NULL, -1, 0, 0}
264 * We have several different classes of counters:
266 * *) runtime counters
268 * *) user-defined counters
269 * *) windows counters (the implementation on windows will use this)
271 * To easily handle the differences we create a vtable for each class that contains the
272 * function pointers with the actual implementation to access the counters.
274 typedef struct _ImplVtable ImplVtable;
276 typedef MonoBoolean (*SampleFunc) (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample* sample);
277 typedef gint64 (*UpdateFunc) (ImplVtable *vtable, MonoBoolean do_incr, gint64 value);
278 typedef void (*CleanupFunc) (ImplVtable *vtable);
294 MonoPerfCounters *counters;
300 SharedInstance *instance_desc;
301 SharedCounter *counter_desc;
305 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
307 ImplVtable *vtable = g_new0 (ImplVtable, 1);
309 vtable->sample = sample;
310 vtable->update = update;
314 MonoPerfCounters *mono_perfcounters = NULL;
315 static MonoSharedArea *shared_area = NULL;
322 /* maps a pid to a ExternalSArea pointer */
323 static GHashTable *pid_to_shared_area = NULL;
325 static MonoSharedArea *
326 load_sarea_for_pid (int pid)
329 MonoSharedArea *area = NULL;
332 if (pid_to_shared_area == NULL)
333 pid_to_shared_area = g_hash_table_new (NULL, NULL);
334 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
336 area = mono_shared_area_for_pid (GINT_TO_POINTER (pid));
338 data = g_new (ExternalSArea, 1);
341 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
352 unref_pid_unlocked (int pid)
355 data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
358 if (!data->refcount) {
359 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
360 mono_shared_area_unload (data->sarea);
367 predef_cleanup (ImplVtable *vtable)
369 PredefVtable *vt = (PredefVtable*)vtable;
370 /* ExternalSArea *data; */
373 if (!pid_to_shared_area) {
377 unref_pid_unlocked (vt->pid);
382 mono_perfcounters_init (void)
384 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
388 InitializeCriticalSection (&perfctr_mutex);
390 shared_area = mono_shared_area ();
391 shared_area->counters_start = G_STRUCT_OFFSET (MonoSharedArea, counters);
392 shared_area->counters_size = sizeof (MonoPerfCounters);
393 shared_area->data_start = d_offset;
394 shared_area->size = 4096;
395 mono_perfcounters = &shared_area->counters;
399 perfctr_type_compress (int type)
402 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
403 if (simple_type_to_type [i] == type)
406 /* NumberOfItems32 */
410 static unsigned char*
411 shared_data_find_room (int size)
413 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
414 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
419 unsigned short *next;
420 if (*p == FTYPE_END) {
421 if (size < (end - p))
427 next = (unsigned short*)(p + 2);
428 if (*p == FTYPE_DELETED) {
429 /* we reuse only if it's the same size */
439 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
442 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
445 unsigned short *next;
448 next = (unsigned short*)(p + 2);
449 if (!func ((SharedHeader*)p, data))
458 foreach_shared_item (SharedFunc func, void *data)
460 unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
461 unsigned char *end = (unsigned char *)shared_area + shared_area->size;
463 foreach_shared_item_in_area (p, end, func, data);
467 mono_string_compare_ascii (MonoString *str, const char *ascii_str)
469 /* FIXME: make this case insensitive */
470 guint16 *strc = mono_string_chars (str);
471 while (*strc == *ascii_str++) {
476 return *strc - *(const unsigned char *)(ascii_str - 1);
485 category_search (SharedHeader *header, void *data)
487 CatSearch *search = data;
488 if (header->ftype == FTYPE_CATEGORY) {
489 SharedCategory *cat = (SharedCategory*)header;
490 if (mono_string_compare_ascii (search->name, cat->name) == 0) {
498 static SharedCategory*
499 find_custom_category (MonoString *name)
504 foreach_shared_item (category_search, &search);
509 category_collect (SharedHeader *header, void *data)
511 GSList **list = data;
512 if (header->ftype == FTYPE_CATEGORY) {
513 *list = g_slist_prepend (*list, header);
519 get_custom_categories (void) {
521 foreach_shared_item (category_collect, &list);
526 custom_category_counters (SharedCategory* cat)
528 char *p = cat->name + strlen (cat->name) + 1;
529 p += strlen (p) + 1; /* skip category help */
533 static SharedCounter*
534 find_custom_counter (SharedCategory* cat, MonoString *name)
537 char *p = custom_category_counters (cat);
538 for (i = 0; i < cat->num_counters; ++i) {
539 SharedCounter *counter = (SharedCounter*)p;
540 if (mono_string_compare_ascii (name, counter->name) == 0)
542 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
543 p += strlen (p) + 1; /* skip counter help */
549 unsigned int cat_offset;
551 MonoString *instance;
552 SharedInstance* result;
557 instance_search (SharedHeader *header, void *data)
559 InstanceSearch *search = data;
560 if (header->ftype == FTYPE_INSTANCE) {
561 SharedInstance *ins = (SharedInstance*)header;
562 if (search->cat_offset == ins->category_offset) {
563 if (search->instance) {
564 if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
565 search->result = ins;
569 search->list = g_slist_prepend (search->list, ins);
576 static SharedInstance*
577 find_custom_instance (SharedCategory* cat, MonoString *instance)
579 InstanceSearch search;
580 search.cat_offset = (char*)cat - (char*)shared_area;
582 search.instance = instance;
584 search.result = NULL;
585 foreach_shared_item (instance_search, &search);
586 return search.result;
590 get_custom_instances_list (SharedCategory* cat)
592 InstanceSearch search;
593 search.cat_offset = (char*)cat - (char*)shared_area;
595 search.instance = NULL;
597 search.result = NULL;
598 foreach_shared_item (instance_search, &search);
603 custom_category_help (SharedCategory* cat)
605 return cat->name + strlen (cat->name) + 1;
608 static const CounterDesc*
609 get_counter_in_category (const CategoryDesc *desc, MonoString *counter)
611 const CounterDesc *cdesc = &predef_counters [desc->first_counter];
612 const CounterDesc *end = &predef_counters [desc [1].first_counter];
613 for (; cdesc < end; ++cdesc) {
614 if (mono_string_compare_ascii (counter, cdesc->name) == 0)
620 /* fill the info in sample (except the raw value) */
622 fill_sample (MonoCounterSample *sample)
624 sample->timeStamp = mono_100ns_ticks ();
625 sample->timeStamp100nSec = sample->timeStamp;
626 sample->counterTimeStamp = sample->timeStamp;
627 sample->counterFrequency = 10000000;
628 sample->systemFrequency = 10000000;
629 // the real basevalue needs to be get from a different counter...
630 sample->baseValue = 0;
634 id_from_string (MonoString *instance, gboolean is_process)
637 if (mono_string_length (instance)) {
638 char *id_str = mono_string_to_utf8 (instance);
640 id = strtol (id_str, &end, 0);
641 if (end == id_str && !is_process)
649 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
651 MonoProcessError error;
652 int id = GPOINTER_TO_INT (vtable->arg);
656 fill_sample (sample);
657 sample->baseValue = 1;
659 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
661 case COUNTER_CPU_USER_TIME:
662 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
664 case COUNTER_CPU_PRIV_TIME:
665 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
667 case COUNTER_CPU_INTR_TIME:
668 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
670 case COUNTER_CPU_DCP_TIME:
671 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
673 case COUNTER_CPU_PROC_TIME:
674 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
681 cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
683 int id = id_from_string (instance, FALSE) << 5;
684 const CounterDesc *cdesc;
686 /* increase the shift above and the mask also in the implementation functions */
687 //g_assert (32 > desc [1].first_counter - desc->first_counter);
688 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_CPU], counter))) {
690 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
696 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
698 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
699 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
701 fill_sample (sample);
704 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
706 case COUNTER_NETWORK_BYTESRECSEC:
707 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
709 case COUNTER_NETWORK_BYTESSENTSEC:
710 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
712 case COUNTER_NETWORK_BYTESTOTALSEC:
713 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
717 if (error == MONO_NETWORK_ERROR_NONE)
724 network_cleanup (ImplVtable *vtable)
726 NetworkVtableArg *narg;
742 network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
744 const CounterDesc *cdesc;
745 NetworkVtableArg *narg;
748 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
749 narg = g_new0 (NetworkVtableArg, 1);
750 narg->id = cdesc->id;
751 narg->name = mono_string_to_utf8 (instance);
753 vtable = create_vtable (narg, get_network_counter, NULL);
754 vtable->cleanup = network_cleanup;
761 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
763 int id = GPOINTER_TO_INT (vtable->arg);
769 fill_sample (sample);
770 sample->baseValue = 1;
772 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
774 case COUNTER_PROC_USER_TIME:
775 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
777 case COUNTER_PROC_PRIV_TIME:
778 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
780 case COUNTER_PROC_PROC_TIME:
781 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
783 case COUNTER_PROC_THREADS:
784 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
786 case COUNTER_PROC_VBYTES:
787 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
789 case COUNTER_PROC_WSET:
790 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
792 case COUNTER_PROC_PBYTES:
793 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
800 process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
802 int id = id_from_string (instance, TRUE) << 5;
803 const CounterDesc *cdesc;
805 /* increase the shift above and the mask also in the implementation functions */
806 //g_assert (32 > desc [1].first_counter - desc->first_counter);
807 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_PROC], counter))) {
809 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
815 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
817 int id = GPOINTER_TO_INT (vtable->arg);
819 fill_sample (sample);
820 sample->baseValue = 1;
822 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
824 case COUNTER_MEM_NUM_OBJECTS:
825 sample->rawValue = mono_stats.new_object_count;
832 mono_mem_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
834 const CounterDesc *cdesc;
836 if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
838 return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
844 predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
846 PredefVtable *vt = (PredefVtable *)vtable;
847 const CounterDesc *desc;
848 int cat_id = GPOINTER_TO_INT (vtable->arg);
849 int id = cat_id >> 16;
852 fill_sample (sample);
853 sample->baseValue = 1;
855 desc = &predef_counters [predef_categories [cat_id].first_counter + id];
856 sample->counterType = desc->type;
857 /* FIXME: check that the offset fits inside imported counters */
858 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
859 sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
864 predef_vtable (void *arg, MonoString *instance)
866 MonoSharedArea *area;
867 PredefVtable *vtable;
868 char *pids = mono_string_to_utf8 (instance);
873 area = load_sarea_for_pid (pid);
877 vtable = g_new (PredefVtable, 1);
878 vtable->vtable.arg = arg;
879 vtable->vtable.sample = predef_readonly_counter;
880 vtable->vtable.cleanup = predef_cleanup;
881 vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
884 return (ImplVtable*)vtable;
887 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
888 * this needs some way to set sample->counterType as well, though.
891 predef_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
893 int cat_id = GPOINTER_TO_INT (vtable->arg);
894 int id = cat_id >> 16;
897 fill_sample (sample);
898 sample->baseValue = 1;
900 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
904 case COUNTER_EXC_THROWN:
905 sample->rawValue = mono_perfcounters->exceptions_thrown;
909 case CATEGORY_ASPNET:
911 case COUNTER_ASPNET_REQ_Q:
912 sample->rawValue = mono_perfcounters->aspnet_requests_queued;
914 case COUNTER_ASPNET_REQ_TOTAL:
915 sample->rawValue = mono_perfcounters->aspnet_requests;
919 case CATEGORY_THREADPOOL:
921 case COUNTER_THREADPOOL_WORKITEMS:
922 sample->rawValue = mono_perfcounters->threadpool_workitems;
924 case COUNTER_THREADPOOL_IOWORKITEMS:
925 sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
934 predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
936 guint32 *volatile ptr = NULL;
937 gint64 *volatile ptr64 = NULL;
938 int cat_id = GPOINTER_TO_INT (vtable->arg);
939 int id = cat_id >> 16;
942 case CATEGORY_ASPNET:
944 case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
945 case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
948 case CATEGORY_THREADPOOL:
950 case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
951 case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
958 return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
960 return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
965 /* this can be non-atomic */
970 /* FIXME: we need to do this atomically */
971 /* No InterlockedIncrement64() yet */
974 return InterlockedIncrement64 (ptr);
976 return InterlockedDecrement64 (ptr);
982 /* this can be non-atomic */
990 predef_writable_get_impl (int cat, MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
992 const CounterDesc *cdesc;
994 if ((cdesc = get_counter_in_category (&predef_categories [cat], counter))) {
996 if (instance == NULL || mono_string_compare_ascii (instance, "") == 0)
997 return create_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), predef_writable_counter, predef_writable_update);
999 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1005 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1007 CustomVTable *counter_data = (CustomVTable *)vtable;
1009 fill_sample (sample);
1010 sample->baseValue = 1;
1012 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1014 sample->rawValue = 0;
1016 sample->rawValue = *(guint64*)vtable->arg;
1021 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1023 /* FIXME: check writability */
1024 guint64 *ptr = vtable->arg;
1027 /* FIXME: we need to do this atomically */
1031 /* this can be non-atomic */
1038 static SharedInstance*
1039 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
1041 SharedInstance* inst;
1044 int size, data_offset;
1046 inst = find_custom_instance (cat, instance);
1049 name = mono_string_to_utf8 (instance);
1050 size = sizeof (SharedInstance) + strlen (name);
1054 size += (sizeof (guint64) * cat->num_counters);
1056 ptr = shared_data_find_room (size);
1062 inst = (SharedInstance*)ptr;
1063 inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
1064 inst->header.size = size;
1065 inst->category_offset = (char*)cat - (char*)shared_area;
1066 cat->num_instances++;
1067 /* now copy the variable data */
1068 p = inst->instance_name;
1070 p += strlen (name) + 1;
1071 inst->header.ftype = FTYPE_INSTANCE;
1079 custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
1081 CustomVTable* vtable;
1082 vtable = g_new0 (CustomVTable, 1);
1083 vtable->vtable.arg = data;
1084 vtable->vtable.sample = custom_writable_counter;
1085 vtable->vtable.update = custom_writable_update;
1086 vtable->instance_desc = inst;
1087 vtable->counter_desc = scounter;
1089 return (ImplVtable*)vtable;
1093 custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
1095 SharedCounter *scounter;
1096 SharedInstance* inst;
1099 scounter = find_custom_counter (cat, counter);
1102 *type = simple_type_to_type [scounter->type];
1103 inst = custom_get_instance (cat, scounter, instance);
1106 size = sizeof (SharedInstance) + strlen (inst->instance_name);
1109 return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
1112 static const CategoryDesc*
1113 find_category (MonoString *category)
1116 for (i = 0; i < NUM_CATEGORIES; ++i) {
1117 if (mono_string_compare_ascii (category, predef_categories [i].name) == 0)
1118 return &predef_categories [i];
1124 mono_perfcounter_get_impl (MonoString* category, MonoString* counter, MonoString* instance,
1125 MonoString* machine, int *type, MonoBoolean *custom)
1127 const CategoryDesc *cdesc;
1128 /* no support for counters on other machines */
1129 if (mono_string_compare_ascii (machine, "."))
1131 cdesc = find_category (category);
1133 SharedCategory *scat = find_custom_category (category);
1137 return custom_get_impl (scat, counter, instance, type);
1139 switch (cdesc->id) {
1141 return cpu_get_impl (counter, instance, type, custom);
1143 return process_get_impl (counter, instance, type, custom);
1144 case CATEGORY_MONO_MEM:
1145 return mono_mem_get_impl (counter, instance, type, custom);
1146 case CATEGORY_NETWORK:
1147 return network_get_impl (counter, instance, type, custom);
1151 case CATEGORY_REMOTING:
1152 case CATEGORY_LOADING:
1153 case CATEGORY_THREAD:
1154 case CATEGORY_INTEROP:
1155 case CATEGORY_SECURITY:
1156 case CATEGORY_ASPNET:
1157 case CATEGORY_THREADPOOL:
1158 return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
1164 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1166 ImplVtable *vtable = impl;
1167 if (vtable && vtable->sample)
1168 return vtable->sample (vtable, only_value, sample);
1173 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1175 ImplVtable *vtable = impl;
1176 if (vtable && vtable->update)
1177 return vtable->update (vtable, do_incr, value);
1182 mono_perfcounter_free_data (void *impl)
1184 ImplVtable *vtable = impl;
1185 if (vtable && vtable->cleanup)
1186 vtable->cleanup (vtable);
1190 /* Category icalls */
1192 mono_perfcounter_category_del (MonoString *name)
1194 const CategoryDesc *cdesc;
1195 SharedCategory *cat;
1196 cdesc = find_category (name);
1197 /* can't delete a predefined category */
1201 cat = find_custom_category (name);
1202 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1203 if (!cat || cat->num_instances) {
1207 cat->header.ftype = FTYPE_DELETED;
1213 mono_perfcounter_category_help (MonoString *category, MonoString *machine)
1215 const CategoryDesc *cdesc;
1216 /* no support for counters on other machines */
1217 if (mono_string_compare_ascii (machine, "."))
1219 cdesc = find_category (category);
1221 SharedCategory *scat = find_custom_category (category);
1224 return mono_string_new (mono_domain_get (), custom_category_help (scat));
1226 return mono_string_new (mono_domain_get (), cdesc->help);
1230 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1231 * TRUE only if a counter with that name exists in the category.
1234 mono_perfcounter_category_exists (MonoString *counter, MonoString *category, MonoString *machine)
1236 const CategoryDesc *cdesc;
1237 /* no support for counters on other machines */
1238 if (mono_string_compare_ascii (machine, "."))
1240 cdesc = find_category (category);
1242 SharedCategory *scat = find_custom_category (category);
1245 /* counter is allowed to be null */
1248 /* search through the custom category */
1249 return find_custom_counter (scat, counter) != NULL;
1251 /* counter is allowed to be null */
1254 if (get_counter_in_category (cdesc, counter))
1259 /* C map of the type with the same name */
1265 } CounterCreationData;
1268 * Since we'll keep a copy of the category per-process, we should also make sure
1269 * categories with the same name are compatible.
1272 mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
1276 int num_counters = mono_array_length (items);
1277 int counters_data_size;
1280 char **counter_info;
1283 SharedCategory *cat;
1285 /* FIXME: ensure there isn't a category created already */
1286 name = mono_string_to_utf8 (category);
1287 chelp = mono_string_to_utf8 (help);
1288 counter_info = g_new0 (char*, num_counters * 2);
1289 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1290 size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
1291 for (i = 0; i < num_counters; ++i) {
1292 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1293 counter_info [i * 2] = mono_string_to_utf8 (data->name);
1294 counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
1295 size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
1297 for (i = 0; i < num_counters * 2; ++i) {
1298 if (!counter_info [i])
1300 size += strlen (counter_info [i]) + 1;
1304 counters_data_size = num_counters * 8; /* optimize for size later */
1308 ptr = shared_data_find_room (size);
1313 cat = (SharedCategory*)ptr;
1314 cat->header.extra = type;
1315 cat->header.size = size;
1316 cat->num_counters = num_counters;
1317 cat->counters_data_size = counters_data_size;
1318 /* now copy the vaiable data */
1321 p += strlen (name) + 1;
1323 p += strlen (chelp) + 1;
1324 for (i = 0; i < num_counters; ++i) {
1325 CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
1326 /* emit the SharedCounter structures */
1327 *p++ = perfctr_type_compress (data->type);
1329 strcpy (p, counter_info [i * 2]);
1330 p += strlen (counter_info [i * 2]) + 1;
1331 strcpy (p, counter_info [i * 2 + 1]);
1332 p += strlen (counter_info [i * 2 + 1]) + 1;
1334 cat->header.ftype = FTYPE_CATEGORY;
1339 for (i = 0; i < num_counters * 2; ++i) {
1340 g_free (counter_info [i]);
1342 g_free (counter_info);
1349 mono_perfcounter_instance_exists (MonoString *instance, MonoString *category, MonoString *machine)
1351 const CategoryDesc *cdesc;
1352 /* no support for counters on other machines */
1353 /*FIXME: machine appears to be wrong
1354 if (mono_string_compare_ascii (machine, "."))
1356 cdesc = find_category (category);
1358 SharedCategory *scat;
1359 scat = find_custom_category (category);
1362 if (find_custom_instance (scat, instance))
1365 /* FIXME: search instance */
1371 mono_perfcounter_category_names (MonoString *machine)
1375 MonoDomain *domain = mono_domain_get ();
1376 GSList *custom_categories, *tmp;
1377 /* no support for counters on other machines */
1378 if (mono_string_compare_ascii (machine, "."))
1379 return mono_array_new (domain, mono_get_string_class (), 0);
1381 custom_categories = get_custom_categories ();
1382 res = mono_array_new (domain, mono_get_string_class (), NUM_CATEGORIES + g_slist_length (custom_categories));
1383 for (i = 0; i < NUM_CATEGORIES; ++i) {
1384 const CategoryDesc *cdesc = &predef_categories [i];
1385 mono_array_setref (res, i, mono_string_new (domain, cdesc->name));
1387 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1388 SharedCategory *scat = tmp->data;
1389 mono_array_setref (res, i, mono_string_new (domain, scat->name));
1393 g_slist_free (custom_categories);
1398 mono_perfcounter_counter_names (MonoString *category, MonoString *machine)
1401 SharedCategory *scat;
1402 const CategoryDesc *cdesc;
1404 MonoDomain *domain = mono_domain_get ();
1405 /* no support for counters on other machines */
1406 if (mono_string_compare_ascii (machine, "."))
1407 return mono_array_new (domain, mono_get_string_class (), 0);
1408 cdesc = find_category (category);
1410 res = mono_array_new (domain, mono_get_string_class (), cdesc [1].first_counter - cdesc->first_counter);
1411 for (i = cdesc->first_counter; i < cdesc [1].first_counter; ++i) {
1412 const CounterDesc *desc = &predef_counters [i];
1413 mono_array_setref (res, i - cdesc->first_counter, mono_string_new (domain, desc->name));
1418 scat = find_custom_category (category);
1420 char *p = custom_category_counters (scat);
1422 res = mono_array_new (domain, mono_get_string_class (), scat->num_counters);
1423 for (i = 0; i < scat->num_counters; ++i) {
1424 mono_array_setref (res, i, mono_string_new (domain, p + 1));
1425 p += 1 + strlen (p + 1) + 1; /* skip counter type and name */
1426 p += strlen (p) + 1; /* skip counter help */
1432 return mono_array_new (domain, mono_get_string_class (), 0);
1436 get_string_array (void **array, int count, gboolean is_process)
1439 MonoDomain *domain = mono_domain_get ();
1440 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1441 for (i = 0; i < count; ++i) {
1445 char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
1446 p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
1448 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1451 mono_array_setref (res, i, mono_string_new (domain, p));
1459 get_string_array_of_strings (void **array, int count)
1462 MonoDomain *domain = mono_domain_get ();
1463 MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
1464 for (i = 0; i < count; ++i) {
1466 mono_array_setref (res, i, mono_string_new (domain, p));
1473 get_mono_instances (void)
1482 buf = g_new (void*, count);
1483 res = mono_shared_area_instances (buf, count);
1484 } while (res == count);
1485 array = get_string_array (buf, res, TRUE);
1491 get_cpu_instances (void)
1496 count = mono_cpu_count ();
1497 buf = g_new (void*, count);
1498 for (i = 0; i < count; ++i)
1499 buf [i] = GINT_TO_POINTER (i);
1500 array = get_string_array (buf, count, FALSE);
1506 get_processes_instances (void)
1510 void **buf = mono_process_list (&count);
1512 return get_string_array (NULL, 0, FALSE);
1513 array = get_string_array (buf, count, TRUE);
1519 get_networkinterface_instances (void)
1523 void **buf = mono_networkinterface_list (&count);
1525 return get_string_array_of_strings (NULL, 0);
1526 array = get_string_array_of_strings (buf, count);
1527 g_strfreev ((char **) buf);
1532 get_custom_instances (MonoString *category)
1534 SharedCategory *scat;
1535 scat = find_custom_category (category);
1537 GSList *list = get_custom_instances_list (scat);
1540 MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
1541 for (tmp = list; tmp; tmp = tmp->next) {
1542 SharedInstance *inst = tmp->data;
1543 mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
1546 g_slist_free (list);
1549 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1553 mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
1555 const CategoryDesc* cat;
1556 if (mono_string_compare_ascii (machine, "."))
1557 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1558 cat = find_category (category);
1560 return get_custom_instances (category);
1561 switch (cat->instance_type) {
1563 return get_mono_instances ();
1565 return get_cpu_instances ();
1566 case ProcessInstance:
1567 return get_processes_instances ();
1568 case NetworkInterfaceInstance:
1569 return get_networkinterface_instances ();
1570 case ThreadInstance:
1572 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);