4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
18 #if defined (__OpenBSD__)
19 #include <sys/param.h>
21 #ifdef HAVE_SYS_TYPES_H
22 #include <sys/types.h>
24 #ifdef HAVE_SYS_TIME_H
27 #if defined (__NetBSD__) || defined (__APPLE__)
28 #include <sys/sysctl.h>
30 #include "metadata/mono-perfcounters.h"
31 #include "metadata/appdomain.h"
32 #include "metadata/object-internals.h"
34 #include "metadata/class-internals.h"
35 #include "utils/mono-time.h"
36 #include "utils/mono-mmap.h"
37 #include "utils/mono-proclib.h"
38 #include "utils/mono-networkinterfaces.h"
39 #include "utils/mono-error-internals.h"
40 #include <mono/io-layer/io-layer.h>
42 /* map of CounterSample.cs */
43 struct _MonoCounterSample
{
46 gint64 counterFrequency
;
47 gint64 systemFrequency
;
49 gint64 timeStamp100nSec
;
50 gint64 counterTimeStamp
;
54 /* map of PerformanceCounterType.cs */
56 NumberOfItemsHEX32
=0x00000000,
57 NumberOfItemsHEX64
=0x00000100,
58 NumberOfItems32
=0x00010000,
59 NumberOfItems64
=0x00010100,
60 CounterDelta32
=0x00400400,
61 CounterDelta64
=0x00400500,
62 SampleCounter
=0x00410400,
63 CountPerTimeInterval32
=0x00450400,
64 CountPerTimeInterval64
=0x00450500,
65 RateOfCountsPerSecond32
=0x10410400,
66 RateOfCountsPerSecond64
=0x10410500,
67 RawFraction
=0x20020400,
68 CounterTimer
=0x20410500,
69 Timer100Ns
=0x20510500,
70 SampleFraction
=0x20C20400,
71 CounterTimerInverse
=0x21410500,
72 Timer100NsInverse
=0x21510500,
73 CounterMultiTimer
=0x22410500,
74 CounterMultiTimer100Ns
=0x22510500,
75 CounterMultiTimerInverse
=0x23410500,
76 CounterMultiTimer100NsInverse
=0x23510500,
77 AverageTimer32
=0x30020400,
78 ElapsedTime
=0x30240500,
79 AverageCount64
=0x40020500,
80 SampleBase
=0x40030401,
81 AverageBase
=0x40030402,
83 CounterMultiBase
=0x42030500
86 /* maps a small integer type to the counter types above */
88 simple_type_to_type
[] = {
89 NumberOfItemsHEX32
, NumberOfItemsHEX64
,
90 NumberOfItems32
, NumberOfItems64
,
91 CounterDelta32
, CounterDelta64
,
92 SampleCounter
, CountPerTimeInterval32
,
93 CountPerTimeInterval64
, RateOfCountsPerSecond32
,
94 RateOfCountsPerSecond64
, RawFraction
,
95 CounterTimer
, Timer100Ns
,
96 SampleFraction
, CounterTimerInverse
,
97 Timer100NsInverse
, CounterMultiTimer
,
98 CounterMultiTimer100Ns
, CounterMultiTimerInverse
,
99 CounterMultiTimer100NsInverse
, AverageTimer32
,
100 ElapsedTime
, AverageCount64
,
101 SampleBase
, AverageBase
,
102 RawBase
, CounterMultiBase
116 NetworkInterfaceInstance
,
120 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
121 #define PERFCTR_COUNTER(id,name,help,type,field)
123 #include "mono-perfcounters-def.h"
128 #undef PERFCTR_COUNTER
129 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
130 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
131 /* each counter is assigned an id starting from 0 inside the category */
133 #include "mono-perfcounters-def.h"
138 #undef PERFCTR_COUNTER
139 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
140 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
141 /* this is used just to count the number of counters */
143 #include "mono-perfcounters-def.h"
147 static CRITICAL_SECTION perfctr_mutex
;
148 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
149 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
154 unsigned short counters_start
;
155 unsigned short counters_size
;
156 unsigned short data_start
;
157 MonoPerfCounters counters
;
162 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
164 struct stanza_header {
165 byte stanza_type; // FTYPE_*
167 ushort stanza_length; // includeas header
172 // perfcat and perfinstance are 4-bytes aligned
176 ushort length; // includes the counters
178 ushort counters_data_size;
180 char name[]; // null terminated
181 char help[]; // null terminated
182 // perfcounters follow
185 char name[]; // null terminated
186 char help[]; // null terminated
191 struct perfinstance {
193 byte data_offset; // offset of counters from beginning of struct
195 uint category_offset; // offset of category in the shared area
196 char name[]; // null terminated
197 // data follows: this is always 8-byte aligned
203 FTYPE_CATEGORY
= 'C',
205 FTYPE_PREDEF_INSTANCE
= 'P', // an instance of a predef counter
206 FTYPE_INSTANCE
= 'I',
219 unsigned short num_counters
;
220 unsigned short counters_data_size
;
222 /* variable length data follows */
228 unsigned int category_offset
;
229 /* variable length data follows */
230 char instance_name
[1];
236 /* variable length data follows */
245 unsigned int instance_type
: 6;
253 unsigned short offset
; // offset inside MonoPerfCounters
258 #undef PERFCTR_COUNTER
259 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
260 #define PERFCTR_COUNTER(id,name,help,type,field)
261 static const CategoryDesc
262 predef_categories
[] = {
263 #include "mono-perfcounters-def.h"
264 {NULL
, NULL
, NUM_CATEGORIES
, -1, 0, NUM_COUNTERS
}
268 #undef PERFCTR_COUNTER
269 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
270 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
271 static const CounterDesc
272 predef_counters
[] = {
273 #include "mono-perfcounters-def.h"
274 {NULL
, NULL
, -1, 0, 0}
278 * We have several different classes of counters:
280 * *) runtime counters
282 * *) user-defined counters
283 * *) windows counters (the implementation on windows will use this)
285 * To easily handle the differences we create a vtable for each class that contains the
286 * function pointers with the actual implementation to access the counters.
288 typedef struct _ImplVtable ImplVtable
;
290 typedef MonoBoolean (*SampleFunc
) (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
* sample
);
291 typedef gint64 (*UpdateFunc
) (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
);
292 typedef void (*CleanupFunc
) (ImplVtable
*vtable
);
308 MonoPerfCounters
*counters
;
314 SharedInstance
*instance_desc
;
315 SharedCounter
*counter_desc
;
319 create_vtable (void *arg
, SampleFunc sample
, UpdateFunc update
)
321 ImplVtable
*vtable
= g_new0 (ImplVtable
, 1);
323 vtable
->sample
= sample
;
324 vtable
->update
= update
;
328 MonoPerfCounters
*mono_perfcounters
= NULL
;
329 static MonoSharedArea
*shared_area
= NULL
;
336 /* maps a pid to a ExternalSArea pointer */
337 static GHashTable
*pid_to_shared_area
= NULL
;
339 static MonoSharedArea
*
340 load_sarea_for_pid (int pid
)
343 MonoSharedArea
*area
= NULL
;
346 if (pid_to_shared_area
== NULL
)
347 pid_to_shared_area
= g_hash_table_new (NULL
, NULL
);
348 data
= g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
350 area
= mono_shared_area_for_pid (GINT_TO_POINTER (pid
));
352 data
= g_new (ExternalSArea
, 1);
355 g_hash_table_insert (pid_to_shared_area
, GINT_TO_POINTER (pid
), data
);
366 unref_pid_unlocked (int pid
)
369 data
= g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
372 if (!data
->refcount
) {
373 g_hash_table_remove (pid_to_shared_area
, GINT_TO_POINTER (pid
));
374 mono_shared_area_unload (data
->sarea
);
381 predef_cleanup (ImplVtable
*vtable
)
383 PredefVtable
*vt
= (PredefVtable
*)vtable
;
384 /* ExternalSArea *data; */
387 if (!pid_to_shared_area
) {
391 unref_pid_unlocked (vt
->pid
);
396 mono_determine_physical_ram_size (void)
398 #if defined (TARGET_WIN32)
399 MEMORYSTATUSEX memstat
;
401 memstat
.dwLength
= sizeof (memstat
);
402 GlobalMemoryStatusEx (&memstat
);
403 return (guint64
)memstat
.ullTotalPhys
;
404 #elif defined (__NetBSD__) || defined (__APPLE__)
418 size_t size_sys
= sizeof (value
);
420 sysctl (mib
, 2, &value
, &size_sys
, NULL
, 0);
424 return (guint64
)value
;
425 #elif defined (HAVE_SYSCONF)
426 guint64 page_size
= 0, num_pages
= 0;
428 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
429 * reports invalid values, please add your OS specific code below. */
431 page_size
= (guint64
)sysconf (_SC_PAGESIZE
);
434 #ifdef _SC_PHYS_PAGES
435 num_pages
= (guint64
)sysconf (_SC_PHYS_PAGES
);
438 if (!page_size
|| !num_pages
) {
439 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
443 return page_size
* num_pages
;
450 mono_perfcounters_init (void)
452 int d_offset
= G_STRUCT_OFFSET (MonoSharedArea
, data
);
456 InitializeCriticalSection (&perfctr_mutex
);
458 shared_area
= mono_shared_area ();
459 shared_area
->counters_start
= G_STRUCT_OFFSET (MonoSharedArea
, counters
);
460 shared_area
->counters_size
= sizeof (MonoPerfCounters
);
461 shared_area
->data_start
= d_offset
;
462 shared_area
->size
= 4096;
463 mono_perfcounters
= &shared_area
->counters
;
467 perfctr_type_compress (int type
)
470 for (i
= 0; i
< G_N_ELEMENTS (simple_type_to_type
); ++i
) {
471 if (simple_type_to_type
[i
] == type
)
474 /* NumberOfItems32 */
478 static unsigned char*
479 shared_data_find_room (int size
)
481 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
482 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
487 unsigned short *next
;
488 if (*p
== FTYPE_END
) {
489 if (size
< (end
- p
))
495 next
= (unsigned short*)(p
+ 2);
496 if (*p
== FTYPE_DELETED
) {
497 /* we reuse only if it's the same size */
507 typedef gboolean (*SharedFunc
) (SharedHeader
*header
, void *data
);
510 foreach_shared_item_in_area (unsigned char *p
, unsigned char *end
, SharedFunc func
, void *data
)
513 unsigned short *next
;
516 next
= (unsigned short*)(p
+ 2);
517 if (!func ((SharedHeader
*)p
, data
))
526 foreach_shared_item (SharedFunc func
, void *data
)
528 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
529 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
531 foreach_shared_item_in_area (p
, end
, func
, data
);
535 mono_string_compare_ascii (MonoString
*str
, const char *ascii_str
)
537 /* FIXME: make this case insensitive */
538 guint16
*strc
= mono_string_chars (str
);
539 while (*strc
== *ascii_str
++) {
544 return *strc
- *(const unsigned char *)(ascii_str
- 1);
553 category_search (SharedHeader
*header
, void *data
)
555 CatSearch
*search
= data
;
556 if (header
->ftype
== FTYPE_CATEGORY
) {
557 SharedCategory
*cat
= (SharedCategory
*)header
;
558 if (mono_string_compare_ascii (search
->name
, cat
->name
) == 0) {
566 static SharedCategory
*
567 find_custom_category (MonoString
*name
)
572 foreach_shared_item (category_search
, &search
);
577 category_collect (SharedHeader
*header
, void *data
)
579 GSList
**list
= data
;
580 if (header
->ftype
== FTYPE_CATEGORY
) {
581 *list
= g_slist_prepend (*list
, header
);
587 get_custom_categories (void) {
589 foreach_shared_item (category_collect
, &list
);
594 custom_category_counters (SharedCategory
* cat
)
596 char *p
= cat
->name
+ strlen (cat
->name
) + 1;
597 p
+= strlen (p
) + 1; /* skip category help */
601 static SharedCounter
*
602 find_custom_counter (SharedCategory
* cat
, MonoString
*name
)
605 char *p
= custom_category_counters (cat
);
606 for (i
= 0; i
< cat
->num_counters
; ++i
) {
607 SharedCounter
*counter
= (SharedCounter
*)p
;
608 if (mono_string_compare_ascii (name
, counter
->name
) == 0)
610 p
+= 1 + strlen (p
+ 1) + 1; /* skip counter type and name */
611 p
+= strlen (p
) + 1; /* skip counter help */
617 unsigned int cat_offset
;
619 MonoString
*instance
;
620 SharedInstance
* result
;
625 instance_search (SharedHeader
*header
, void *data
)
627 InstanceSearch
*search
= data
;
628 if (header
->ftype
== FTYPE_INSTANCE
) {
629 SharedInstance
*ins
= (SharedInstance
*)header
;
630 if (search
->cat_offset
== ins
->category_offset
) {
631 if (search
->instance
) {
632 if (mono_string_compare_ascii (search
->instance
, ins
->instance_name
) == 0) {
633 search
->result
= ins
;
637 search
->list
= g_slist_prepend (search
->list
, ins
);
644 static SharedInstance
*
645 find_custom_instance (SharedCategory
* cat
, MonoString
*instance
)
647 InstanceSearch search
;
648 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
650 search
.instance
= instance
;
652 search
.result
= NULL
;
653 foreach_shared_item (instance_search
, &search
);
654 return search
.result
;
658 get_custom_instances_list (SharedCategory
* cat
)
660 InstanceSearch search
;
661 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
663 search
.instance
= NULL
;
665 search
.result
= NULL
;
666 foreach_shared_item (instance_search
, &search
);
671 custom_category_help (SharedCategory
* cat
)
673 return cat
->name
+ strlen (cat
->name
) + 1;
676 static const CounterDesc
*
677 get_counter_in_category (const CategoryDesc
*desc
, MonoString
*counter
)
679 const CounterDesc
*cdesc
= &predef_counters
[desc
->first_counter
];
680 const CounterDesc
*end
= &predef_counters
[desc
[1].first_counter
];
681 for (; cdesc
< end
; ++cdesc
) {
682 if (mono_string_compare_ascii (counter
, cdesc
->name
) == 0)
688 /* fill the info in sample (except the raw value) */
690 fill_sample (MonoCounterSample
*sample
)
692 sample
->timeStamp
= mono_100ns_ticks ();
693 sample
->timeStamp100nSec
= sample
->timeStamp
;
694 sample
->counterTimeStamp
= sample
->timeStamp
;
695 sample
->counterFrequency
= 10000000;
696 sample
->systemFrequency
= 10000000;
697 // the real basevalue needs to be get from a different counter...
698 sample
->baseValue
= 0;
702 id_from_string (MonoString
*instance
, gboolean is_process
)
705 if (mono_string_length (instance
)) {
706 char *id_str
= mono_string_to_utf8 (instance
);
708 id
= strtol (id_str
, &end
, 0);
709 if (end
== id_str
&& !is_process
)
717 get_cpu_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
719 MonoProcessError error
;
720 int id
= GPOINTER_TO_INT (vtable
->arg
);
724 fill_sample (sample
);
725 sample
->baseValue
= 1;
727 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_CPU
].first_counter
+ id
].type
;
729 case COUNTER_CPU_USER_TIME
:
730 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_USER_TIME
, &error
);
732 case COUNTER_CPU_PRIV_TIME
:
733 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_PRIV_TIME
, &error
);
735 case COUNTER_CPU_INTR_TIME
:
736 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_INTR_TIME
, &error
);
738 case COUNTER_CPU_DCP_TIME
:
739 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_DCP_TIME
, &error
);
741 case COUNTER_CPU_PROC_TIME
:
742 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_IDLE_TIME
, &error
);
749 cpu_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
751 int id
= id_from_string (instance
, FALSE
) << 5;
752 const CounterDesc
*cdesc
;
754 /* increase the shift above and the mask also in the implementation functions */
755 //g_assert (32 > desc [1].first_counter - desc->first_counter);
756 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_CPU
], counter
))) {
758 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_cpu_counter
, NULL
);
764 get_network_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
766 MonoNetworkError error
= MONO_NETWORK_ERROR_OTHER
;
767 NetworkVtableArg
*narg
= (NetworkVtableArg
*) vtable
->arg
;
769 fill_sample (sample
);
772 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_NETWORK
].first_counter
+ narg
->id
].type
;
774 case COUNTER_NETWORK_BYTESRECSEC
:
775 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESREC
, &error
);
777 case COUNTER_NETWORK_BYTESSENTSEC
:
778 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESSENT
, &error
);
780 case COUNTER_NETWORK_BYTESTOTALSEC
:
781 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESTOTAL
, &error
);
785 if (error
== MONO_NETWORK_ERROR_NONE
)
792 network_cleanup (ImplVtable
*vtable
)
794 NetworkVtableArg
*narg
;
810 network_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
812 const CounterDesc
*cdesc
;
813 NetworkVtableArg
*narg
;
818 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_NETWORK
], counter
))) {
819 instance_name
= mono_string_to_utf8 (instance
);
820 narg
= g_new0 (NetworkVtableArg
, 1);
821 narg
->id
= cdesc
->id
;
822 narg
->name
= instance_name
;
824 vtable
= create_vtable (narg
, get_network_counter
, NULL
);
825 vtable
->cleanup
= network_cleanup
;
832 get_process_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
834 int id
= GPOINTER_TO_INT (vtable
->arg
);
840 fill_sample (sample
);
841 sample
->baseValue
= 1;
843 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_PROC
].first_counter
+ id
].type
;
845 case COUNTER_PROC_USER_TIME
:
846 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_USER_TIME
);
848 case COUNTER_PROC_PRIV_TIME
:
849 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_SYSTEM_TIME
);
851 case COUNTER_PROC_PROC_TIME
:
852 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_TOTAL_TIME
);
854 case COUNTER_PROC_THREADS
:
855 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_NUM_THREADS
);
857 case COUNTER_PROC_VBYTES
:
858 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_VIRTUAL_BYTES
);
860 case COUNTER_PROC_WSET
:
861 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_WORKING_SET
);
863 case COUNTER_PROC_PBYTES
:
864 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_PRIVATE_BYTES
);
871 process_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
873 int id
= id_from_string (instance
, TRUE
) << 5;
874 const CounterDesc
*cdesc
;
876 /* increase the shift above and the mask also in the implementation functions */
877 //g_assert (32 > desc [1].first_counter - desc->first_counter);
878 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_PROC
], counter
))) {
880 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_process_counter
, NULL
);
886 mono_mem_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
888 int id
= GPOINTER_TO_INT (vtable
->arg
);
890 fill_sample (sample
);
891 sample
->baseValue
= 1;
893 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_MONO_MEM
].first_counter
+ id
].type
;
895 case COUNTER_MEM_NUM_OBJECTS
:
896 sample
->rawValue
= mono_stats
.new_object_count
;
898 case COUNTER_MEM_PHYS_TOTAL
:
899 sample
->rawValue
= mono_determine_physical_ram_size ();;
906 mono_mem_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
908 const CounterDesc
*cdesc
;
910 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_MONO_MEM
], counter
))) {
912 return create_vtable (GINT_TO_POINTER ((gint
) cdesc
->id
), mono_mem_counter
, NULL
);
918 predef_readonly_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
920 PredefVtable
*vt
= (PredefVtable
*)vtable
;
921 const CounterDesc
*desc
;
922 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
923 int id
= cat_id
>> 16;
926 fill_sample (sample
);
927 sample
->baseValue
= 1;
929 desc
= &predef_counters
[predef_categories
[cat_id
].first_counter
+ id
];
930 sample
->counterType
= desc
->type
;
931 /* FIXME: check that the offset fits inside imported counters */
932 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
933 sample
->rawValue
= *(guint32
*)((char*)vt
->counters
+ desc
->offset
);
938 predef_vtable (void *arg
, MonoString
*instance
)
940 MonoSharedArea
*area
;
941 PredefVtable
*vtable
;
942 char *pids
= mono_string_to_utf8 (instance
);
947 area
= load_sarea_for_pid (pid
);
951 vtable
= g_new (PredefVtable
, 1);
952 vtable
->vtable
.arg
= arg
;
953 vtable
->vtable
.sample
= predef_readonly_counter
;
954 vtable
->vtable
.cleanup
= predef_cleanup
;
955 vtable
->counters
= (MonoPerfCounters
*)((char*)area
+ area
->counters_start
);
958 return (ImplVtable
*)vtable
;
961 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
962 * this needs some way to set sample->counterType as well, though.
965 predef_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
967 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
968 int id
= cat_id
>> 16;
971 fill_sample (sample
);
972 sample
->baseValue
= 1;
974 sample
->counterType
= predef_counters
[predef_categories
[cat_id
].first_counter
+ id
].type
;
978 case COUNTER_EXC_THROWN
:
979 sample
->rawValue
= mono_perfcounters
->exceptions_thrown
;
983 case CATEGORY_ASPNET
:
985 case COUNTER_ASPNET_REQ_Q
:
986 sample
->rawValue
= mono_perfcounters
->aspnet_requests_queued
;
988 case COUNTER_ASPNET_REQ_TOTAL
:
989 sample
->rawValue
= mono_perfcounters
->aspnet_requests
;
993 case CATEGORY_THREADPOOL
:
995 case COUNTER_THREADPOOL_WORKITEMS
:
996 sample
->rawValue
= mono_perfcounters
->threadpool_workitems
;
998 case COUNTER_THREADPOOL_IOWORKITEMS
:
999 sample
->rawValue
= mono_perfcounters
->threadpool_ioworkitems
;
1001 case COUNTER_THREADPOOL_THREADS
:
1002 sample
->rawValue
= mono_perfcounters
->threadpool_threads
;
1004 case COUNTER_THREADPOOL_IOTHREADS
:
1005 sample
->rawValue
= mono_perfcounters
->threadpool_iothreads
;
1014 predef_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
1016 guint32
*volatile ptr
= NULL
;
1017 gint64
*volatile ptr64
= NULL
;
1018 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
1019 int id
= cat_id
>> 16;
1022 case CATEGORY_ASPNET
:
1024 case COUNTER_ASPNET_REQ_Q
: ptr
= &mono_perfcounters
->aspnet_requests_queued
; break;
1025 case COUNTER_ASPNET_REQ_TOTAL
: ptr
= &mono_perfcounters
->aspnet_requests
; break;
1028 case CATEGORY_THREADPOOL
:
1030 case COUNTER_THREADPOOL_WORKITEMS
: ptr64
= (gint64
*) &mono_perfcounters
->threadpool_workitems
; break;
1031 case COUNTER_THREADPOOL_IOWORKITEMS
: ptr64
= (gint64
*) &mono_perfcounters
->threadpool_ioworkitems
; break;
1032 case COUNTER_THREADPOOL_THREADS
: ptr
= &mono_perfcounters
->threadpool_threads
; break;
1033 case COUNTER_THREADPOOL_IOTHREADS
: ptr
= &mono_perfcounters
->threadpool_iothreads
; break;
1040 return InterlockedIncrement ((gint32
*) ptr
); /* FIXME: sign */
1042 return InterlockedDecrement ((gint32
*) ptr
); /* FIXME: sign */
1047 /* this can be non-atomic */
1052 /* FIXME: we need to do this atomically */
1053 /* No InterlockedIncrement64() yet */
1056 return InterlockedIncrement64 (ptr);
1058 return InterlockedDecrement64 (ptr);
1064 /* this can be non-atomic */
1072 predef_writable_get_impl (int cat
, MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
1074 const CounterDesc
*cdesc
;
1076 if ((cdesc
= get_counter_in_category (&predef_categories
[cat
], counter
))) {
1077 *type
= cdesc
->type
;
1078 if (instance
== NULL
|| mono_string_compare_ascii (instance
, "") == 0)
1079 return create_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), predef_writable_counter
, predef_writable_update
);
1081 return predef_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), instance
);
1087 custom_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1089 CustomVTable
*counter_data
= (CustomVTable
*)vtable
;
1091 fill_sample (sample
);
1092 sample
->baseValue
= 1;
1094 sample
->counterType
= simple_type_to_type
[counter_data
->counter_desc
->type
];
1096 sample
->rawValue
= 0;
1098 sample
->rawValue
= *(guint64
*)vtable
->arg
;
1103 custom_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
1105 /* FIXME: check writability */
1106 guint64
*ptr
= vtable
->arg
;
1109 /* FIXME: we need to do this atomically */
1113 /* this can be non-atomic */
1120 static SharedInstance
*
1121 custom_get_instance (SharedCategory
*cat
, SharedCounter
*scounter
, MonoString
* instance
)
1123 SharedInstance
* inst
;
1126 int size
, data_offset
;
1128 inst
= find_custom_instance (cat
, instance
);
1131 name
= mono_string_to_utf8 (instance
);
1132 size
= sizeof (SharedInstance
) + strlen (name
);
1136 size
+= (sizeof (guint64
) * cat
->num_counters
);
1138 ptr
= shared_data_find_room (size
);
1144 inst
= (SharedInstance
*)ptr
;
1145 inst
->header
.extra
= 0; /* data_offset could overflow here, so we leave this field unused */
1146 inst
->header
.size
= size
;
1147 inst
->category_offset
= (char*)cat
- (char*)shared_area
;
1148 cat
->num_instances
++;
1149 /* now copy the variable data */
1150 p
= inst
->instance_name
;
1152 p
+= strlen (name
) + 1;
1153 inst
->header
.ftype
= FTYPE_INSTANCE
;
1161 custom_vtable (SharedCounter
*scounter
, SharedInstance
* inst
, char *data
)
1163 CustomVTable
* vtable
;
1164 vtable
= g_new0 (CustomVTable
, 1);
1165 vtable
->vtable
.arg
= data
;
1166 vtable
->vtable
.sample
= custom_writable_counter
;
1167 vtable
->vtable
.update
= custom_writable_update
;
1168 vtable
->instance_desc
= inst
;
1169 vtable
->counter_desc
= scounter
;
1171 return (ImplVtable
*)vtable
;
1175 custom_get_impl (SharedCategory
*cat
, MonoString
* counter
, MonoString
* instance
, int *type
)
1177 SharedCounter
*scounter
;
1178 SharedInstance
* inst
;
1181 scounter
= find_custom_counter (cat
, counter
);
1184 *type
= simple_type_to_type
[scounter
->type
];
1185 inst
= custom_get_instance (cat
, scounter
, instance
);
1188 size
= sizeof (SharedInstance
) + strlen (inst
->instance_name
);
1191 return custom_vtable (scounter
, inst
, (char*)inst
+ size
+ scounter
->seq_num
* sizeof (guint64
));
1194 static const CategoryDesc
*
1195 find_category (MonoString
*category
)
1198 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1199 if (mono_string_compare_ascii (category
, predef_categories
[i
].name
) == 0)
1200 return &predef_categories
[i
];
1206 mono_perfcounter_get_impl (MonoString
* category
, MonoString
* counter
, MonoString
* instance
,
1207 MonoString
* machine
, int *type
, MonoBoolean
*custom
)
1209 const CategoryDesc
*cdesc
;
1210 /* no support for counters on other machines */
1211 if (mono_string_compare_ascii (machine
, "."))
1213 cdesc
= find_category (category
);
1215 SharedCategory
*scat
= find_custom_category (category
);
1219 return custom_get_impl (scat
, counter
, instance
, type
);
1221 switch (cdesc
->id
) {
1223 return cpu_get_impl (counter
, instance
, type
, custom
);
1225 return process_get_impl (counter
, instance
, type
, custom
);
1226 case CATEGORY_MONO_MEM
:
1227 return mono_mem_get_impl (counter
, instance
, type
, custom
);
1228 case CATEGORY_NETWORK
:
1229 return network_get_impl (counter
, instance
, type
, custom
);
1233 case CATEGORY_REMOTING
:
1234 case CATEGORY_LOADING
:
1235 case CATEGORY_THREAD
:
1236 case CATEGORY_INTEROP
:
1237 case CATEGORY_SECURITY
:
1238 case CATEGORY_ASPNET
:
1239 case CATEGORY_THREADPOOL
:
1240 return predef_writable_get_impl (cdesc
->id
, counter
, instance
, type
, custom
);
1246 mono_perfcounter_get_sample (void *impl
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1248 ImplVtable
*vtable
= impl
;
1249 if (vtable
&& vtable
->sample
)
1250 return vtable
->sample (vtable
, only_value
, sample
);
1255 mono_perfcounter_update_value (void *impl
, MonoBoolean do_incr
, gint64 value
)
1257 ImplVtable
*vtable
= impl
;
1258 if (vtable
&& vtable
->update
)
1259 return vtable
->update (vtable
, do_incr
, value
);
1264 mono_perfcounter_free_data (void *impl
)
1266 ImplVtable
*vtable
= impl
;
1267 if (vtable
&& vtable
->cleanup
)
1268 vtable
->cleanup (vtable
);
1272 /* Category icalls */
1274 mono_perfcounter_category_del (MonoString
*name
)
1276 const CategoryDesc
*cdesc
;
1277 SharedCategory
*cat
;
1278 cdesc
= find_category (name
);
1279 /* can't delete a predefined category */
1283 cat
= find_custom_category (name
);
1284 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1285 if (!cat
|| cat
->num_instances
) {
1289 cat
->header
.ftype
= FTYPE_DELETED
;
1295 mono_perfcounter_category_help (MonoString
*category
, MonoString
*machine
)
1297 const CategoryDesc
*cdesc
;
1298 /* no support for counters on other machines */
1299 if (mono_string_compare_ascii (machine
, "."))
1301 cdesc
= find_category (category
);
1303 SharedCategory
*scat
= find_custom_category (category
);
1306 return mono_string_new (mono_domain_get (), custom_category_help (scat
));
1308 return mono_string_new (mono_domain_get (), cdesc
->help
);
1312 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1313 * TRUE only if a counter with that name exists in the category.
1316 mono_perfcounter_category_exists (MonoString
*counter
, MonoString
*category
, MonoString
*machine
)
1318 const CategoryDesc
*cdesc
;
1319 /* no support for counters on other machines */
1320 if (mono_string_compare_ascii (machine
, "."))
1322 cdesc
= find_category (category
);
1324 SharedCategory
*scat
= find_custom_category (category
);
1327 /* counter is allowed to be null */
1330 /* search through the custom category */
1331 return find_custom_counter (scat
, counter
) != NULL
;
1333 /* counter is allowed to be null */
1336 if (get_counter_in_category (cdesc
, counter
))
1341 /* C map of the type with the same name */
1347 } CounterCreationData
;
1350 * Since we'll keep a copy of the category per-process, we should also make sure
1351 * categories with the same name are compatible.
1354 mono_perfcounter_create (MonoString
*category
, MonoString
*help
, int type
, MonoArray
*items
)
1359 int num_counters
= mono_array_length (items
);
1360 int counters_data_size
;
1363 char **counter_info
= NULL
;
1366 SharedCategory
*cat
;
1368 /* FIXME: ensure there isn't a category created already */
1369 mono_error_init (&error
);
1370 name
= mono_string_to_utf8_checked (category
, &error
);
1371 if (!mono_error_ok (&error
))
1373 chelp
= mono_string_to_utf8_checked (help
, &error
);
1374 if (!mono_error_ok (&error
))
1376 counter_info
= g_new0 (char*, num_counters
* 2);
1377 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1378 size
= G_STRUCT_OFFSET (SharedCategory
, name
) + strlen (name
) + strlen (chelp
) + 2;
1379 for (i
= 0; i
< num_counters
; ++i
) {
1380 CounterCreationData
*data
= mono_array_get (items
, CounterCreationData
*, i
);
1381 counter_info
[i
* 2] = mono_string_to_utf8_checked (data
->name
, &error
);
1382 if (!mono_error_ok (&error
))
1384 counter_info
[i
* 2 + 1] = mono_string_to_utf8_checked (data
->help
, &error
);
1385 if (!mono_error_ok (&error
))
1387 size
+= sizeof (SharedCounter
) + 1; /* 1 is for the help 0 terminator */
1389 for (i
= 0; i
< num_counters
* 2; ++i
) {
1390 if (!counter_info
[i
])
1392 size
+= strlen (counter_info
[i
]) + 1;
1396 counters_data_size
= num_counters
* 8; /* optimize for size later */
1400 ptr
= shared_data_find_room (size
);
1405 cat
= (SharedCategory
*)ptr
;
1406 cat
->header
.extra
= type
;
1407 cat
->header
.size
= size
;
1408 cat
->num_counters
= num_counters
;
1409 cat
->counters_data_size
= counters_data_size
;
1410 /* now copy the vaiable data */
1413 p
+= strlen (name
) + 1;
1415 p
+= strlen (chelp
) + 1;
1416 for (i
= 0; i
< num_counters
; ++i
) {
1417 CounterCreationData
*data
= mono_array_get (items
, CounterCreationData
*, i
);
1418 /* emit the SharedCounter structures */
1419 *p
++ = perfctr_type_compress (data
->type
);
1421 strcpy (p
, counter_info
[i
* 2]);
1422 p
+= strlen (counter_info
[i
* 2]) + 1;
1423 strcpy (p
, counter_info
[i
* 2 + 1]);
1424 p
+= strlen (counter_info
[i
* 2 + 1]) + 1;
1426 cat
->header
.ftype
= FTYPE_CATEGORY
;
1432 for (i
= 0; i
< num_counters
* 2; ++i
) {
1433 g_free (counter_info
[i
]);
1435 g_free (counter_info
);
1439 mono_error_cleanup (&error
);
1444 mono_perfcounter_instance_exists (MonoString
*instance
, MonoString
*category
, MonoString
*machine
)
1446 const CategoryDesc
*cdesc
;
1447 /* no support for counters on other machines */
1448 /*FIXME: machine appears to be wrong
1449 if (mono_string_compare_ascii (machine, "."))
1451 cdesc
= find_category (category
);
1453 SharedCategory
*scat
;
1454 scat
= find_custom_category (category
);
1457 if (find_custom_instance (scat
, instance
))
1460 /* FIXME: search instance */
1466 mono_perfcounter_category_names (MonoString
*machine
)
1470 MonoDomain
*domain
= mono_domain_get ();
1471 GSList
*custom_categories
, *tmp
;
1472 /* no support for counters on other machines */
1473 if (mono_string_compare_ascii (machine
, "."))
1474 return mono_array_new (domain
, mono_get_string_class (), 0);
1476 custom_categories
= get_custom_categories ();
1477 res
= mono_array_new (domain
, mono_get_string_class (), NUM_CATEGORIES
+ g_slist_length (custom_categories
));
1478 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1479 const CategoryDesc
*cdesc
= &predef_categories
[i
];
1480 mono_array_setref (res
, i
, mono_string_new (domain
, cdesc
->name
));
1482 for (tmp
= custom_categories
; tmp
; tmp
= tmp
->next
) {
1483 SharedCategory
*scat
= tmp
->data
;
1484 mono_array_setref (res
, i
, mono_string_new (domain
, scat
->name
));
1488 g_slist_free (custom_categories
);
1493 mono_perfcounter_counter_names (MonoString
*category
, MonoString
*machine
)
1496 SharedCategory
*scat
;
1497 const CategoryDesc
*cdesc
;
1499 MonoDomain
*domain
= mono_domain_get ();
1500 /* no support for counters on other machines */
1501 if (mono_string_compare_ascii (machine
, "."))
1502 return mono_array_new (domain
, mono_get_string_class (), 0);
1503 cdesc
= find_category (category
);
1505 res
= mono_array_new (domain
, mono_get_string_class (), cdesc
[1].first_counter
- cdesc
->first_counter
);
1506 for (i
= cdesc
->first_counter
; i
< cdesc
[1].first_counter
; ++i
) {
1507 const CounterDesc
*desc
= &predef_counters
[i
];
1508 mono_array_setref (res
, i
- cdesc
->first_counter
, mono_string_new (domain
, desc
->name
));
1513 scat
= find_custom_category (category
);
1515 char *p
= custom_category_counters (scat
);
1517 res
= mono_array_new (domain
, mono_get_string_class (), scat
->num_counters
);
1518 for (i
= 0; i
< scat
->num_counters
; ++i
) {
1519 mono_array_setref (res
, i
, mono_string_new (domain
, p
+ 1));
1520 p
+= 1 + strlen (p
+ 1) + 1; /* skip counter type and name */
1521 p
+= strlen (p
) + 1; /* skip counter help */
1527 return mono_array_new (domain
, mono_get_string_class (), 0);
1531 get_string_array (void **array
, int count
, gboolean is_process
)
1534 MonoDomain
*domain
= mono_domain_get ();
1535 MonoArray
* res
= mono_array_new (mono_domain_get (), mono_get_string_class (), count
);
1536 for (i
= 0; i
< count
; ++i
) {
1540 char *pname
= mono_process_get_name (array
[i
], buf
, sizeof (buf
));
1541 p
= g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array
[i
]), pname
);
1543 sprintf (buf
, "%d", GPOINTER_TO_INT (array
[i
]));
1546 mono_array_setref (res
, i
, mono_string_new (domain
, p
));
1554 get_string_array_of_strings (void **array
, int count
)
1557 MonoDomain
*domain
= mono_domain_get ();
1558 MonoArray
* res
= mono_array_new (mono_domain_get (), mono_get_string_class (), count
);
1559 for (i
= 0; i
< count
; ++i
) {
1561 mono_array_setref (res
, i
, mono_string_new (domain
, p
));
1568 get_mono_instances (void)
1577 buf
= g_new (void*, count
);
1578 res
= mono_shared_area_instances (buf
, count
);
1579 } while (res
== count
);
1580 array
= get_string_array (buf
, res
, TRUE
);
1586 get_cpu_instances (void)
1592 count
= mono_cpu_count () + 1; /* +1 for "_Total" */
1593 buf
= g_new (void*, count
);
1594 for (i
= 0; i
< count
; ++i
)
1595 buf
[i
] = GINT_TO_POINTER (i
- 1); /* -1 => _Total */
1596 array
= get_string_array (buf
, count
, FALSE
);
1598 mono_array_setref (array
, 0, mono_string_new (mono_domain_get (), "_Total"));
1603 get_processes_instances (void)
1607 void **buf
= mono_process_list (&count
);
1609 return get_string_array (NULL
, 0, FALSE
);
1610 array
= get_string_array (buf
, count
, TRUE
);
1616 get_networkinterface_instances (void)
1620 void **buf
= mono_networkinterface_list (&count
);
1622 return get_string_array_of_strings (NULL
, 0);
1623 array
= get_string_array_of_strings (buf
, count
);
1624 g_strfreev ((char **) buf
);
1629 get_custom_instances (MonoString
*category
)
1631 SharedCategory
*scat
;
1632 scat
= find_custom_category (category
);
1634 GSList
*list
= get_custom_instances_list (scat
);
1637 MonoArray
*array
= mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list
));
1638 for (tmp
= list
; tmp
; tmp
= tmp
->next
) {
1639 SharedInstance
*inst
= tmp
->data
;
1640 mono_array_setref (array
, i
, mono_string_new (mono_domain_get (), inst
->instance_name
));
1643 g_slist_free (list
);
1646 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1650 mono_perfcounter_instance_names (MonoString
*category
, MonoString
*machine
)
1652 const CategoryDesc
* cat
;
1653 if (mono_string_compare_ascii (machine
, "."))
1654 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1655 cat
= find_category (category
);
1657 return get_custom_instances (category
);
1658 switch (cat
->instance_type
) {
1660 return get_mono_instances ();
1662 return get_cpu_instances ();
1663 case ProcessInstance
:
1664 return get_processes_instances ();
1665 case NetworkInterfaceInstance
:
1666 return get_networkinterface_instances ();
1667 case ThreadInstance
:
1669 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);