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"
20 #include "metadata/object-internals.h"
22 #include "metadata/class-internals.h"
23 #include "utils/mono-time.h"
24 #include "utils/mono-mmap.h"
25 #include "utils/mono-proclib.h"
26 #include "utils/mono-networkinterfaces.h"
27 #include "utils/mono-error-internals.h"
28 #include <mono/io-layer/io-layer.h>
30 /* map of CounterSample.cs */
31 struct _MonoCounterSample
{
34 gint64 counterFrequency
;
35 gint64 systemFrequency
;
37 gint64 timeStamp100nSec
;
38 gint64 counterTimeStamp
;
42 /* map of PerformanceCounterType.cs */
44 NumberOfItemsHEX32
=0x00000000,
45 NumberOfItemsHEX64
=0x00000100,
46 NumberOfItems32
=0x00010000,
47 NumberOfItems64
=0x00010100,
48 CounterDelta32
=0x00400400,
49 CounterDelta64
=0x00400500,
50 SampleCounter
=0x00410400,
51 CountPerTimeInterval32
=0x00450400,
52 CountPerTimeInterval64
=0x00450500,
53 RateOfCountsPerSecond32
=0x10410400,
54 RateOfCountsPerSecond64
=0x10410500,
55 RawFraction
=0x20020400,
56 CounterTimer
=0x20410500,
57 Timer100Ns
=0x20510500,
58 SampleFraction
=0x20C20400,
59 CounterTimerInverse
=0x21410500,
60 Timer100NsInverse
=0x21510500,
61 CounterMultiTimer
=0x22410500,
62 CounterMultiTimer100Ns
=0x22510500,
63 CounterMultiTimerInverse
=0x23410500,
64 CounterMultiTimer100NsInverse
=0x23510500,
65 AverageTimer32
=0x30020400,
66 ElapsedTime
=0x30240500,
67 AverageCount64
=0x40020500,
68 SampleBase
=0x40030401,
69 AverageBase
=0x40030402,
71 CounterMultiBase
=0x42030500
74 /* maps a small integer type to the counter types above */
76 simple_type_to_type
[] = {
77 NumberOfItemsHEX32
, NumberOfItemsHEX64
,
78 NumberOfItems32
, NumberOfItems64
,
79 CounterDelta32
, CounterDelta64
,
80 SampleCounter
, CountPerTimeInterval32
,
81 CountPerTimeInterval64
, RateOfCountsPerSecond32
,
82 RateOfCountsPerSecond64
, RawFraction
,
83 CounterTimer
, Timer100Ns
,
84 SampleFraction
, CounterTimerInverse
,
85 Timer100NsInverse
, CounterMultiTimer
,
86 CounterMultiTimer100Ns
, CounterMultiTimerInverse
,
87 CounterMultiTimer100NsInverse
, AverageTimer32
,
88 ElapsedTime
, AverageCount64
,
89 SampleBase
, AverageBase
,
90 RawBase
, CounterMultiBase
104 NetworkInterfaceInstance
,
108 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
109 #define PERFCTR_COUNTER(id,name,help,type,field)
111 #include "mono-perfcounters-def.h"
116 #undef PERFCTR_COUNTER
117 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
118 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
119 /* each counter is assigned an id starting from 0 inside the category */
121 #include "mono-perfcounters-def.h"
126 #undef PERFCTR_COUNTER
127 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
128 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
129 /* this is used just to count the number of counters */
131 #include "mono-perfcounters-def.h"
135 static CRITICAL_SECTION perfctr_mutex
;
136 #define perfctr_lock() EnterCriticalSection (&perfctr_mutex)
137 #define perfctr_unlock() LeaveCriticalSection (&perfctr_mutex)
142 unsigned short counters_start
;
143 unsigned short counters_size
;
144 unsigned short data_start
;
145 MonoPerfCounters counters
;
150 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
152 struct stanza_header {
153 byte stanza_type; // FTYPE_*
155 ushort stanza_length; // includeas header
160 // perfcat and perfinstance are 4-bytes aligned
164 ushort length; // includes the counters
166 ushort counters_data_size;
168 char name[]; // null terminated
169 char help[]; // null terminated
170 // perfcounters follow
173 char name[]; // null terminated
174 char help[]; // null terminated
179 struct perfinstance {
181 byte data_offset; // offset of counters from beginning of struct
183 uint category_offset; // offset of category in the shared area
184 char name[]; // null terminated
185 // data follows: this is always 8-byte aligned
191 FTYPE_CATEGORY
= 'C',
193 FTYPE_PREDEF_INSTANCE
= 'P', // an instance of a predef counter
194 FTYPE_INSTANCE
= 'I',
207 unsigned short num_counters
;
208 unsigned short counters_data_size
;
210 /* variable length data follows */
216 unsigned int category_offset
;
217 /* variable length data follows */
218 char instance_name
[1];
224 /* variable length data follows */
233 unsigned int instance_type
: 6;
241 unsigned short offset
; // offset inside MonoPerfCounters
246 #undef PERFCTR_COUNTER
247 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
248 #define PERFCTR_COUNTER(id,name,help,type,field)
249 static const CategoryDesc
250 predef_categories
[] = {
251 #include "mono-perfcounters-def.h"
252 {NULL
, NULL
, NUM_CATEGORIES
, -1, 0, NUM_COUNTERS
}
256 #undef PERFCTR_COUNTER
257 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
258 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
259 static const CounterDesc
260 predef_counters
[] = {
261 #include "mono-perfcounters-def.h"
262 {NULL
, NULL
, -1, 0, 0}
266 * We have several different classes of counters:
268 * *) runtime counters
270 * *) user-defined counters
271 * *) windows counters (the implementation on windows will use this)
273 * To easily handle the differences we create a vtable for each class that contains the
274 * function pointers with the actual implementation to access the counters.
276 typedef struct _ImplVtable ImplVtable
;
278 typedef MonoBoolean (*SampleFunc
) (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
* sample
);
279 typedef gint64 (*UpdateFunc
) (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
);
280 typedef void (*CleanupFunc
) (ImplVtable
*vtable
);
296 MonoPerfCounters
*counters
;
302 SharedInstance
*instance_desc
;
303 SharedCounter
*counter_desc
;
307 create_vtable (void *arg
, SampleFunc sample
, UpdateFunc update
)
309 ImplVtable
*vtable
= g_new0 (ImplVtable
, 1);
311 vtable
->sample
= sample
;
312 vtable
->update
= update
;
316 MonoPerfCounters
*mono_perfcounters
= NULL
;
317 static MonoSharedArea
*shared_area
= NULL
;
324 /* maps a pid to a ExternalSArea pointer */
325 static GHashTable
*pid_to_shared_area
= NULL
;
327 static MonoSharedArea
*
328 load_sarea_for_pid (int pid
)
331 MonoSharedArea
*area
= NULL
;
334 if (pid_to_shared_area
== NULL
)
335 pid_to_shared_area
= g_hash_table_new (NULL
, NULL
);
336 data
= g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
338 area
= mono_shared_area_for_pid (GINT_TO_POINTER (pid
));
340 data
= g_new (ExternalSArea
, 1);
343 g_hash_table_insert (pid_to_shared_area
, GINT_TO_POINTER (pid
), data
);
354 unref_pid_unlocked (int pid
)
357 data
= g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
360 if (!data
->refcount
) {
361 g_hash_table_remove (pid_to_shared_area
, GINT_TO_POINTER (pid
));
362 mono_shared_area_unload (data
->sarea
);
369 predef_cleanup (ImplVtable
*vtable
)
371 PredefVtable
*vt
= (PredefVtable
*)vtable
;
372 /* ExternalSArea *data; */
375 if (!pid_to_shared_area
) {
379 unref_pid_unlocked (vt
->pid
);
384 mono_perfcounters_init (void)
386 int d_offset
= G_STRUCT_OFFSET (MonoSharedArea
, data
);
390 InitializeCriticalSection (&perfctr_mutex
);
392 shared_area
= mono_shared_area ();
393 shared_area
->counters_start
= G_STRUCT_OFFSET (MonoSharedArea
, counters
);
394 shared_area
->counters_size
= sizeof (MonoPerfCounters
);
395 shared_area
->data_start
= d_offset
;
396 shared_area
->size
= 4096;
397 mono_perfcounters
= &shared_area
->counters
;
401 perfctr_type_compress (int type
)
404 for (i
= 0; i
< G_N_ELEMENTS (simple_type_to_type
); ++i
) {
405 if (simple_type_to_type
[i
] == type
)
408 /* NumberOfItems32 */
412 static unsigned char*
413 shared_data_find_room (int size
)
415 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
416 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
421 unsigned short *next
;
422 if (*p
== FTYPE_END
) {
423 if (size
< (end
- p
))
429 next
= (unsigned short*)(p
+ 2);
430 if (*p
== FTYPE_DELETED
) {
431 /* we reuse only if it's the same size */
441 typedef gboolean (*SharedFunc
) (SharedHeader
*header
, void *data
);
444 foreach_shared_item_in_area (unsigned char *p
, unsigned char *end
, SharedFunc func
, void *data
)
447 unsigned short *next
;
450 next
= (unsigned short*)(p
+ 2);
451 if (!func ((SharedHeader
*)p
, data
))
460 foreach_shared_item (SharedFunc func
, void *data
)
462 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
463 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
465 foreach_shared_item_in_area (p
, end
, func
, data
);
469 mono_string_compare_ascii (MonoString
*str
, const char *ascii_str
)
471 /* FIXME: make this case insensitive */
472 guint16
*strc
= mono_string_chars (str
);
473 while (*strc
== *ascii_str
++) {
478 return *strc
- *(const unsigned char *)(ascii_str
- 1);
487 category_search (SharedHeader
*header
, void *data
)
489 CatSearch
*search
= data
;
490 if (header
->ftype
== FTYPE_CATEGORY
) {
491 SharedCategory
*cat
= (SharedCategory
*)header
;
492 if (mono_string_compare_ascii (search
->name
, cat
->name
) == 0) {
500 static SharedCategory
*
501 find_custom_category (MonoString
*name
)
506 foreach_shared_item (category_search
, &search
);
511 category_collect (SharedHeader
*header
, void *data
)
513 GSList
**list
= data
;
514 if (header
->ftype
== FTYPE_CATEGORY
) {
515 *list
= g_slist_prepend (*list
, header
);
521 get_custom_categories (void) {
523 foreach_shared_item (category_collect
, &list
);
528 custom_category_counters (SharedCategory
* cat
)
530 char *p
= cat
->name
+ strlen (cat
->name
) + 1;
531 p
+= strlen (p
) + 1; /* skip category help */
535 static SharedCounter
*
536 find_custom_counter (SharedCategory
* cat
, MonoString
*name
)
539 char *p
= custom_category_counters (cat
);
540 for (i
= 0; i
< cat
->num_counters
; ++i
) {
541 SharedCounter
*counter
= (SharedCounter
*)p
;
542 if (mono_string_compare_ascii (name
, counter
->name
) == 0)
544 p
+= 1 + strlen (p
+ 1) + 1; /* skip counter type and name */
545 p
+= strlen (p
) + 1; /* skip counter help */
551 unsigned int cat_offset
;
553 MonoString
*instance
;
554 SharedInstance
* result
;
559 instance_search (SharedHeader
*header
, void *data
)
561 InstanceSearch
*search
= data
;
562 if (header
->ftype
== FTYPE_INSTANCE
) {
563 SharedInstance
*ins
= (SharedInstance
*)header
;
564 if (search
->cat_offset
== ins
->category_offset
) {
565 if (search
->instance
) {
566 if (mono_string_compare_ascii (search
->instance
, ins
->instance_name
) == 0) {
567 search
->result
= ins
;
571 search
->list
= g_slist_prepend (search
->list
, ins
);
578 static SharedInstance
*
579 find_custom_instance (SharedCategory
* cat
, MonoString
*instance
)
581 InstanceSearch search
;
582 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
584 search
.instance
= instance
;
586 search
.result
= NULL
;
587 foreach_shared_item (instance_search
, &search
);
588 return search
.result
;
592 get_custom_instances_list (SharedCategory
* cat
)
594 InstanceSearch search
;
595 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
597 search
.instance
= NULL
;
599 search
.result
= NULL
;
600 foreach_shared_item (instance_search
, &search
);
605 custom_category_help (SharedCategory
* cat
)
607 return cat
->name
+ strlen (cat
->name
) + 1;
610 static const CounterDesc
*
611 get_counter_in_category (const CategoryDesc
*desc
, MonoString
*counter
)
613 const CounterDesc
*cdesc
= &predef_counters
[desc
->first_counter
];
614 const CounterDesc
*end
= &predef_counters
[desc
[1].first_counter
];
615 for (; cdesc
< end
; ++cdesc
) {
616 if (mono_string_compare_ascii (counter
, cdesc
->name
) == 0)
622 /* fill the info in sample (except the raw value) */
624 fill_sample (MonoCounterSample
*sample
)
626 sample
->timeStamp
= mono_100ns_ticks ();
627 sample
->timeStamp100nSec
= sample
->timeStamp
;
628 sample
->counterTimeStamp
= sample
->timeStamp
;
629 sample
->counterFrequency
= 10000000;
630 sample
->systemFrequency
= 10000000;
631 // the real basevalue needs to be get from a different counter...
632 sample
->baseValue
= 0;
636 id_from_string (MonoString
*instance
, gboolean is_process
)
639 if (mono_string_length (instance
)) {
640 char *id_str
= mono_string_to_utf8 (instance
);
642 id
= strtol (id_str
, &end
, 0);
643 if (end
== id_str
&& !is_process
)
651 get_cpu_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
653 MonoProcessError error
;
654 int id
= GPOINTER_TO_INT (vtable
->arg
);
658 fill_sample (sample
);
659 sample
->baseValue
= 1;
661 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_CPU
].first_counter
+ id
].type
;
663 case COUNTER_CPU_USER_TIME
:
664 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_USER_TIME
, &error
);
666 case COUNTER_CPU_PRIV_TIME
:
667 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_PRIV_TIME
, &error
);
669 case COUNTER_CPU_INTR_TIME
:
670 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_INTR_TIME
, &error
);
672 case COUNTER_CPU_DCP_TIME
:
673 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_DCP_TIME
, &error
);
675 case COUNTER_CPU_PROC_TIME
:
676 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_IDLE_TIME
, &error
);
683 cpu_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
685 int id
= id_from_string (instance
, FALSE
) << 5;
686 const CounterDesc
*cdesc
;
688 /* increase the shift above and the mask also in the implementation functions */
689 //g_assert (32 > desc [1].first_counter - desc->first_counter);
690 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_CPU
], counter
))) {
692 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_cpu_counter
, NULL
);
698 get_network_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
700 MonoNetworkError error
= MONO_NETWORK_ERROR_OTHER
;
701 NetworkVtableArg
*narg
= (NetworkVtableArg
*) vtable
->arg
;
703 fill_sample (sample
);
706 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_NETWORK
].first_counter
+ narg
->id
].type
;
708 case COUNTER_NETWORK_BYTESRECSEC
:
709 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESREC
, &error
);
711 case COUNTER_NETWORK_BYTESSENTSEC
:
712 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESSENT
, &error
);
714 case COUNTER_NETWORK_BYTESTOTALSEC
:
715 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESTOTAL
, &error
);
719 if (error
== MONO_NETWORK_ERROR_NONE
)
726 network_cleanup (ImplVtable
*vtable
)
728 NetworkVtableArg
*narg
;
744 network_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
746 const CounterDesc
*cdesc
;
747 NetworkVtableArg
*narg
;
752 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_NETWORK
], counter
))) {
753 instance_name
= mono_string_to_utf8 (instance
);
754 narg
= g_new0 (NetworkVtableArg
, 1);
755 narg
->id
= cdesc
->id
;
756 narg
->name
= instance_name
;
758 vtable
= create_vtable (narg
, get_network_counter
, NULL
);
759 vtable
->cleanup
= network_cleanup
;
766 get_process_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
768 int id
= GPOINTER_TO_INT (vtable
->arg
);
774 fill_sample (sample
);
775 sample
->baseValue
= 1;
777 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_PROC
].first_counter
+ id
].type
;
779 case COUNTER_PROC_USER_TIME
:
780 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_USER_TIME
);
782 case COUNTER_PROC_PRIV_TIME
:
783 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_SYSTEM_TIME
);
785 case COUNTER_PROC_PROC_TIME
:
786 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_TOTAL_TIME
);
788 case COUNTER_PROC_THREADS
:
789 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_NUM_THREADS
);
791 case COUNTER_PROC_VBYTES
:
792 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_VIRTUAL_BYTES
);
794 case COUNTER_PROC_WSET
:
795 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_WORKING_SET
);
797 case COUNTER_PROC_PBYTES
:
798 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_PRIVATE_BYTES
);
805 process_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
807 int id
= id_from_string (instance
, TRUE
) << 5;
808 const CounterDesc
*cdesc
;
810 /* increase the shift above and the mask also in the implementation functions */
811 //g_assert (32 > desc [1].first_counter - desc->first_counter);
812 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_PROC
], counter
))) {
814 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_process_counter
, NULL
);
820 mono_mem_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
822 int id
= GPOINTER_TO_INT (vtable
->arg
);
824 fill_sample (sample
);
825 sample
->baseValue
= 1;
827 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_MONO_MEM
].first_counter
+ id
].type
;
829 case COUNTER_MEM_NUM_OBJECTS
:
830 sample
->rawValue
= mono_stats
.new_object_count
;
837 mono_mem_get_impl (MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
839 const CounterDesc
*cdesc
;
841 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_MONO_MEM
], counter
))) {
843 return create_vtable (GINT_TO_POINTER ((gint
) cdesc
->id
), mono_mem_counter
, NULL
);
849 predef_readonly_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
851 PredefVtable
*vt
= (PredefVtable
*)vtable
;
852 const CounterDesc
*desc
;
853 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
854 int id
= cat_id
>> 16;
857 fill_sample (sample
);
858 sample
->baseValue
= 1;
860 desc
= &predef_counters
[predef_categories
[cat_id
].first_counter
+ id
];
861 sample
->counterType
= desc
->type
;
862 /* FIXME: check that the offset fits inside imported counters */
863 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
864 sample
->rawValue
= *(guint32
*)((char*)vt
->counters
+ desc
->offset
);
869 predef_vtable (void *arg
, MonoString
*instance
)
871 MonoSharedArea
*area
;
872 PredefVtable
*vtable
;
873 char *pids
= mono_string_to_utf8 (instance
);
878 area
= load_sarea_for_pid (pid
);
882 vtable
= g_new (PredefVtable
, 1);
883 vtable
->vtable
.arg
= arg
;
884 vtable
->vtable
.sample
= predef_readonly_counter
;
885 vtable
->vtable
.cleanup
= predef_cleanup
;
886 vtable
->counters
= (MonoPerfCounters
*)((char*)area
+ area
->counters_start
);
889 return (ImplVtable
*)vtable
;
892 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
893 * this needs some way to set sample->counterType as well, though.
896 predef_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
898 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
899 int id
= cat_id
>> 16;
902 fill_sample (sample
);
903 sample
->baseValue
= 1;
905 sample
->counterType
= predef_counters
[predef_categories
[cat_id
].first_counter
+ id
].type
;
909 case COUNTER_EXC_THROWN
:
910 sample
->rawValue
= mono_perfcounters
->exceptions_thrown
;
914 case CATEGORY_ASPNET
:
916 case COUNTER_ASPNET_REQ_Q
:
917 sample
->rawValue
= mono_perfcounters
->aspnet_requests_queued
;
919 case COUNTER_ASPNET_REQ_TOTAL
:
920 sample
->rawValue
= mono_perfcounters
->aspnet_requests
;
924 case CATEGORY_THREADPOOL
:
926 case COUNTER_THREADPOOL_WORKITEMS
:
927 sample
->rawValue
= mono_perfcounters
->threadpool_workitems
;
929 case COUNTER_THREADPOOL_IOWORKITEMS
:
930 sample
->rawValue
= mono_perfcounters
->threadpool_ioworkitems
;
932 case COUNTER_THREADPOOL_THREADS
:
933 sample
->rawValue
= mono_perfcounters
->threadpool_threads
;
935 case COUNTER_THREADPOOL_IOTHREADS
:
936 sample
->rawValue
= mono_perfcounters
->threadpool_iothreads
;
945 predef_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
947 guint32
*volatile ptr
= NULL
;
948 gint64
*volatile ptr64
= NULL
;
949 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
950 int id
= cat_id
>> 16;
953 case CATEGORY_ASPNET
:
955 case COUNTER_ASPNET_REQ_Q
: ptr
= &mono_perfcounters
->aspnet_requests_queued
; break;
956 case COUNTER_ASPNET_REQ_TOTAL
: ptr
= &mono_perfcounters
->aspnet_requests
; break;
959 case CATEGORY_THREADPOOL
:
961 case COUNTER_THREADPOOL_WORKITEMS
: ptr64
= (gint64
*) &mono_perfcounters
->threadpool_workitems
; break;
962 case COUNTER_THREADPOOL_IOWORKITEMS
: ptr64
= (gint64
*) &mono_perfcounters
->threadpool_ioworkitems
; break;
963 case COUNTER_THREADPOOL_THREADS
: ptr
= &mono_perfcounters
->threadpool_threads
; break;
964 case COUNTER_THREADPOOL_IOTHREADS
: ptr
= &mono_perfcounters
->threadpool_iothreads
; break;
971 return InterlockedIncrement ((gint32
*) ptr
); /* FIXME: sign */
973 return InterlockedDecrement ((gint32
*) ptr
); /* FIXME: sign */
978 /* this can be non-atomic */
983 /* FIXME: we need to do this atomically */
984 /* No InterlockedIncrement64() yet */
987 return InterlockedIncrement64 (ptr);
989 return InterlockedDecrement64 (ptr);
995 /* this can be non-atomic */
1003 predef_writable_get_impl (int cat
, MonoString
* counter
, MonoString
* instance
, int *type
, MonoBoolean
*custom
)
1005 const CounterDesc
*cdesc
;
1007 if ((cdesc
= get_counter_in_category (&predef_categories
[cat
], counter
))) {
1008 *type
= cdesc
->type
;
1009 if (instance
== NULL
|| mono_string_compare_ascii (instance
, "") == 0)
1010 return create_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), predef_writable_counter
, predef_writable_update
);
1012 return predef_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), instance
);
1018 custom_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1020 CustomVTable
*counter_data
= (CustomVTable
*)vtable
;
1022 fill_sample (sample
);
1023 sample
->baseValue
= 1;
1025 sample
->counterType
= simple_type_to_type
[counter_data
->counter_desc
->type
];
1027 sample
->rawValue
= 0;
1029 sample
->rawValue
= *(guint64
*)vtable
->arg
;
1034 custom_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
1036 /* FIXME: check writability */
1037 guint64
*ptr
= vtable
->arg
;
1040 /* FIXME: we need to do this atomically */
1044 /* this can be non-atomic */
1051 static SharedInstance
*
1052 custom_get_instance (SharedCategory
*cat
, SharedCounter
*scounter
, MonoString
* instance
)
1054 SharedInstance
* inst
;
1057 int size
, data_offset
;
1059 inst
= find_custom_instance (cat
, instance
);
1062 name
= mono_string_to_utf8 (instance
);
1063 size
= sizeof (SharedInstance
) + strlen (name
);
1067 size
+= (sizeof (guint64
) * cat
->num_counters
);
1069 ptr
= shared_data_find_room (size
);
1075 inst
= (SharedInstance
*)ptr
;
1076 inst
->header
.extra
= 0; /* data_offset could overflow here, so we leave this field unused */
1077 inst
->header
.size
= size
;
1078 inst
->category_offset
= (char*)cat
- (char*)shared_area
;
1079 cat
->num_instances
++;
1080 /* now copy the variable data */
1081 p
= inst
->instance_name
;
1083 p
+= strlen (name
) + 1;
1084 inst
->header
.ftype
= FTYPE_INSTANCE
;
1092 custom_vtable (SharedCounter
*scounter
, SharedInstance
* inst
, char *data
)
1094 CustomVTable
* vtable
;
1095 vtable
= g_new0 (CustomVTable
, 1);
1096 vtable
->vtable
.arg
= data
;
1097 vtable
->vtable
.sample
= custom_writable_counter
;
1098 vtable
->vtable
.update
= custom_writable_update
;
1099 vtable
->instance_desc
= inst
;
1100 vtable
->counter_desc
= scounter
;
1102 return (ImplVtable
*)vtable
;
1106 custom_get_impl (SharedCategory
*cat
, MonoString
* counter
, MonoString
* instance
, int *type
)
1108 SharedCounter
*scounter
;
1109 SharedInstance
* inst
;
1112 scounter
= find_custom_counter (cat
, counter
);
1115 *type
= simple_type_to_type
[scounter
->type
];
1116 inst
= custom_get_instance (cat
, scounter
, instance
);
1119 size
= sizeof (SharedInstance
) + strlen (inst
->instance_name
);
1122 return custom_vtable (scounter
, inst
, (char*)inst
+ size
+ scounter
->seq_num
* sizeof (guint64
));
1125 static const CategoryDesc
*
1126 find_category (MonoString
*category
)
1129 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1130 if (mono_string_compare_ascii (category
, predef_categories
[i
].name
) == 0)
1131 return &predef_categories
[i
];
1137 mono_perfcounter_get_impl (MonoString
* category
, MonoString
* counter
, MonoString
* instance
,
1138 MonoString
* machine
, int *type
, MonoBoolean
*custom
)
1140 const CategoryDesc
*cdesc
;
1141 /* no support for counters on other machines */
1142 if (mono_string_compare_ascii (machine
, "."))
1144 cdesc
= find_category (category
);
1146 SharedCategory
*scat
= find_custom_category (category
);
1150 return custom_get_impl (scat
, counter
, instance
, type
);
1152 switch (cdesc
->id
) {
1154 return cpu_get_impl (counter
, instance
, type
, custom
);
1156 return process_get_impl (counter
, instance
, type
, custom
);
1157 case CATEGORY_MONO_MEM
:
1158 return mono_mem_get_impl (counter
, instance
, type
, custom
);
1159 case CATEGORY_NETWORK
:
1160 return network_get_impl (counter
, instance
, type
, custom
);
1164 case CATEGORY_REMOTING
:
1165 case CATEGORY_LOADING
:
1166 case CATEGORY_THREAD
:
1167 case CATEGORY_INTEROP
:
1168 case CATEGORY_SECURITY
:
1169 case CATEGORY_ASPNET
:
1170 case CATEGORY_THREADPOOL
:
1171 return predef_writable_get_impl (cdesc
->id
, counter
, instance
, type
, custom
);
1177 mono_perfcounter_get_sample (void *impl
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1179 ImplVtable
*vtable
= impl
;
1180 if (vtable
&& vtable
->sample
)
1181 return vtable
->sample (vtable
, only_value
, sample
);
1186 mono_perfcounter_update_value (void *impl
, MonoBoolean do_incr
, gint64 value
)
1188 ImplVtable
*vtable
= impl
;
1189 if (vtable
&& vtable
->update
)
1190 return vtable
->update (vtable
, do_incr
, value
);
1195 mono_perfcounter_free_data (void *impl
)
1197 ImplVtable
*vtable
= impl
;
1198 if (vtable
&& vtable
->cleanup
)
1199 vtable
->cleanup (vtable
);
1203 /* Category icalls */
1205 mono_perfcounter_category_del (MonoString
*name
)
1207 const CategoryDesc
*cdesc
;
1208 SharedCategory
*cat
;
1209 cdesc
= find_category (name
);
1210 /* can't delete a predefined category */
1214 cat
= find_custom_category (name
);
1215 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1216 if (!cat
|| cat
->num_instances
) {
1220 cat
->header
.ftype
= FTYPE_DELETED
;
1226 mono_perfcounter_category_help (MonoString
*category
, MonoString
*machine
)
1228 const CategoryDesc
*cdesc
;
1229 /* no support for counters on other machines */
1230 if (mono_string_compare_ascii (machine
, "."))
1232 cdesc
= find_category (category
);
1234 SharedCategory
*scat
= find_custom_category (category
);
1237 return mono_string_new (mono_domain_get (), custom_category_help (scat
));
1239 return mono_string_new (mono_domain_get (), cdesc
->help
);
1243 * Check if the category named @category exists on @machine. If @counter is not NULL, return
1244 * TRUE only if a counter with that name exists in the category.
1247 mono_perfcounter_category_exists (MonoString
*counter
, MonoString
*category
, MonoString
*machine
)
1249 const CategoryDesc
*cdesc
;
1250 /* no support for counters on other machines */
1251 if (mono_string_compare_ascii (machine
, "."))
1253 cdesc
= find_category (category
);
1255 SharedCategory
*scat
= find_custom_category (category
);
1258 /* counter is allowed to be null */
1261 /* search through the custom category */
1262 return find_custom_counter (scat
, counter
) != NULL
;
1264 /* counter is allowed to be null */
1267 if (get_counter_in_category (cdesc
, counter
))
1272 /* C map of the type with the same name */
1278 } CounterCreationData
;
1281 * Since we'll keep a copy of the category per-process, we should also make sure
1282 * categories with the same name are compatible.
1285 mono_perfcounter_create (MonoString
*category
, MonoString
*help
, int type
, MonoArray
*items
)
1290 int num_counters
= mono_array_length (items
);
1291 int counters_data_size
;
1294 char **counter_info
= NULL
;
1297 SharedCategory
*cat
;
1299 /* FIXME: ensure there isn't a category created already */
1300 mono_error_init (&error
);
1301 name
= mono_string_to_utf8_checked (category
, &error
);
1302 if (!mono_error_ok (&error
))
1304 chelp
= mono_string_to_utf8_checked (help
, &error
);
1305 if (!mono_error_ok (&error
))
1307 counter_info
= g_new0 (char*, num_counters
* 2);
1308 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1309 size
= G_STRUCT_OFFSET (SharedCategory
, name
) + strlen (name
) + strlen (chelp
) + 2;
1310 for (i
= 0; i
< num_counters
; ++i
) {
1311 CounterCreationData
*data
= mono_array_get (items
, CounterCreationData
*, i
);
1312 counter_info
[i
* 2] = mono_string_to_utf8_checked (data
->name
, &error
);
1313 if (!mono_error_ok (&error
))
1315 counter_info
[i
* 2 + 1] = mono_string_to_utf8_checked (data
->help
, &error
);
1316 if (!mono_error_ok (&error
))
1318 size
+= sizeof (SharedCounter
) + 1; /* 1 is for the help 0 terminator */
1320 for (i
= 0; i
< num_counters
* 2; ++i
) {
1321 if (!counter_info
[i
])
1323 size
+= strlen (counter_info
[i
]) + 1;
1327 counters_data_size
= num_counters
* 8; /* optimize for size later */
1331 ptr
= shared_data_find_room (size
);
1336 cat
= (SharedCategory
*)ptr
;
1337 cat
->header
.extra
= type
;
1338 cat
->header
.size
= size
;
1339 cat
->num_counters
= num_counters
;
1340 cat
->counters_data_size
= counters_data_size
;
1341 /* now copy the vaiable data */
1344 p
+= strlen (name
) + 1;
1346 p
+= strlen (chelp
) + 1;
1347 for (i
= 0; i
< num_counters
; ++i
) {
1348 CounterCreationData
*data
= mono_array_get (items
, CounterCreationData
*, i
);
1349 /* emit the SharedCounter structures */
1350 *p
++ = perfctr_type_compress (data
->type
);
1352 strcpy (p
, counter_info
[i
* 2]);
1353 p
+= strlen (counter_info
[i
* 2]) + 1;
1354 strcpy (p
, counter_info
[i
* 2 + 1]);
1355 p
+= strlen (counter_info
[i
* 2 + 1]) + 1;
1357 cat
->header
.ftype
= FTYPE_CATEGORY
;
1363 for (i
= 0; i
< num_counters
* 2; ++i
) {
1364 g_free (counter_info
[i
]);
1366 g_free (counter_info
);
1370 mono_error_cleanup (&error
);
1375 mono_perfcounter_instance_exists (MonoString
*instance
, MonoString
*category
, MonoString
*machine
)
1377 const CategoryDesc
*cdesc
;
1378 /* no support for counters on other machines */
1379 /*FIXME: machine appears to be wrong
1380 if (mono_string_compare_ascii (machine, "."))
1382 cdesc
= find_category (category
);
1384 SharedCategory
*scat
;
1385 scat
= find_custom_category (category
);
1388 if (find_custom_instance (scat
, instance
))
1391 /* FIXME: search instance */
1397 mono_perfcounter_category_names (MonoString
*machine
)
1401 MonoDomain
*domain
= mono_domain_get ();
1402 GSList
*custom_categories
, *tmp
;
1403 /* no support for counters on other machines */
1404 if (mono_string_compare_ascii (machine
, "."))
1405 return mono_array_new (domain
, mono_get_string_class (), 0);
1407 custom_categories
= get_custom_categories ();
1408 res
= mono_array_new (domain
, mono_get_string_class (), NUM_CATEGORIES
+ g_slist_length (custom_categories
));
1409 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1410 const CategoryDesc
*cdesc
= &predef_categories
[i
];
1411 mono_array_setref (res
, i
, mono_string_new (domain
, cdesc
->name
));
1413 for (tmp
= custom_categories
; tmp
; tmp
= tmp
->next
) {
1414 SharedCategory
*scat
= tmp
->data
;
1415 mono_array_setref (res
, i
, mono_string_new (domain
, scat
->name
));
1419 g_slist_free (custom_categories
);
1424 mono_perfcounter_counter_names (MonoString
*category
, MonoString
*machine
)
1427 SharedCategory
*scat
;
1428 const CategoryDesc
*cdesc
;
1430 MonoDomain
*domain
= mono_domain_get ();
1431 /* no support for counters on other machines */
1432 if (mono_string_compare_ascii (machine
, "."))
1433 return mono_array_new (domain
, mono_get_string_class (), 0);
1434 cdesc
= find_category (category
);
1436 res
= mono_array_new (domain
, mono_get_string_class (), cdesc
[1].first_counter
- cdesc
->first_counter
);
1437 for (i
= cdesc
->first_counter
; i
< cdesc
[1].first_counter
; ++i
) {
1438 const CounterDesc
*desc
= &predef_counters
[i
];
1439 mono_array_setref (res
, i
- cdesc
->first_counter
, mono_string_new (domain
, desc
->name
));
1444 scat
= find_custom_category (category
);
1446 char *p
= custom_category_counters (scat
);
1448 res
= mono_array_new (domain
, mono_get_string_class (), scat
->num_counters
);
1449 for (i
= 0; i
< scat
->num_counters
; ++i
) {
1450 mono_array_setref (res
, i
, mono_string_new (domain
, p
+ 1));
1451 p
+= 1 + strlen (p
+ 1) + 1; /* skip counter type and name */
1452 p
+= strlen (p
) + 1; /* skip counter help */
1458 return mono_array_new (domain
, mono_get_string_class (), 0);
1462 get_string_array (void **array
, int count
, gboolean is_process
)
1465 MonoDomain
*domain
= mono_domain_get ();
1466 MonoArray
* res
= mono_array_new (mono_domain_get (), mono_get_string_class (), count
);
1467 for (i
= 0; i
< count
; ++i
) {
1471 char *pname
= mono_process_get_name (array
[i
], buf
, sizeof (buf
));
1472 p
= g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array
[i
]), pname
);
1474 sprintf (buf
, "%d", GPOINTER_TO_INT (array
[i
]));
1477 mono_array_setref (res
, i
, mono_string_new (domain
, p
));
1485 get_string_array_of_strings (void **array
, int count
)
1488 MonoDomain
*domain
= mono_domain_get ();
1489 MonoArray
* res
= mono_array_new (mono_domain_get (), mono_get_string_class (), count
);
1490 for (i
= 0; i
< count
; ++i
) {
1492 mono_array_setref (res
, i
, mono_string_new (domain
, p
));
1499 get_mono_instances (void)
1508 buf
= g_new (void*, count
);
1509 res
= mono_shared_area_instances (buf
, count
);
1510 } while (res
== count
);
1511 array
= get_string_array (buf
, res
, TRUE
);
1517 get_cpu_instances (void)
1523 count
= mono_cpu_count () + 1; /* +1 for "_Total" */
1524 buf
= g_new (void*, count
);
1525 for (i
= 0; i
< count
; ++i
)
1526 buf
[i
] = GINT_TO_POINTER (i
- 1); /* -1 => _Total */
1527 array
= get_string_array (buf
, count
, FALSE
);
1529 mono_array_setref (array
, 0, mono_string_new (mono_domain_get (), "_Total"));
1534 get_processes_instances (void)
1538 void **buf
= mono_process_list (&count
);
1540 return get_string_array (NULL
, 0, FALSE
);
1541 array
= get_string_array (buf
, count
, TRUE
);
1547 get_networkinterface_instances (void)
1551 void **buf
= mono_networkinterface_list (&count
);
1553 return get_string_array_of_strings (NULL
, 0);
1554 array
= get_string_array_of_strings (buf
, count
);
1555 g_strfreev ((char **) buf
);
1560 get_custom_instances (MonoString
*category
)
1562 SharedCategory
*scat
;
1563 scat
= find_custom_category (category
);
1565 GSList
*list
= get_custom_instances_list (scat
);
1568 MonoArray
*array
= mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list
));
1569 for (tmp
= list
; tmp
; tmp
= tmp
->next
) {
1570 SharedInstance
*inst
= tmp
->data
;
1571 mono_array_setref (array
, i
, mono_string_new (mono_domain_get (), inst
->instance_name
));
1574 g_slist_free (list
);
1577 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1581 mono_perfcounter_instance_names (MonoString
*category
, MonoString
*machine
)
1583 const CategoryDesc
* cat
;
1584 if (mono_string_compare_ascii (machine
, "."))
1585 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
1586 cat
= find_category (category
);
1588 return get_custom_instances (category
);
1589 switch (cat
->instance_type
) {
1591 return get_mono_instances ();
1593 return get_cpu_instances ();
1594 case ProcessInstance
:
1595 return get_processes_instances ();
1596 case NetworkInterfaceInstance
:
1597 return get_networkinterface_instances ();
1598 case ThreadInstance
:
1600 return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);