4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #if defined (__OpenBSD__)
21 #include <sys/param.h>
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
26 #ifdef HAVE_SYS_TIME_H
29 #if defined (__APPLE__)
30 #include <mach/message.h>
31 #include <mach/mach_host.h>
32 #include <mach/host_info.h>
33 #include <sys/sysctl.h>
35 #if defined (__NetBSD__)
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <sys/vmmeter.h>
40 #include "metadata/mono-perfcounters.h"
41 #include "metadata/appdomain.h"
42 #include "metadata/object-internals.h"
44 #include "metadata/class-internals.h"
45 #include "utils/mono-time.h"
46 #include "utils/mono-mmap.h"
47 #include "utils/mono-proclib.h"
48 #include "utils/mono-networkinterfaces.h"
49 #include "utils/mono-error-internals.h"
50 #include "utils/atomic.h"
51 #include "utils/unlocked.h"
52 #include "icall-decl.h"
54 /* map of CounterSample.cs */
55 struct _MonoCounterSample
{
58 gint64 counterFrequency
;
59 gint64 systemFrequency
;
61 gint64 timeStamp100nSec
;
62 gint64 counterTimeStamp
;
66 #ifndef DISABLE_PERFCOUNTERS
67 /* map of PerformanceCounterType.cs */
69 NumberOfItemsHEX32
=0x00000000,
70 NumberOfItemsHEX64
=0x00000100,
71 NumberOfItems32
=0x00010000,
72 NumberOfItems64
=0x00010100,
73 CounterDelta32
=0x00400400,
74 CounterDelta64
=0x00400500,
75 SampleCounter
=0x00410400,
76 CountPerTimeInterval32
=0x00450400,
77 CountPerTimeInterval64
=0x00450500,
78 RateOfCountsPerSecond32
=0x10410400,
79 RateOfCountsPerSecond64
=0x10410500,
80 RawFraction
=0x20020400,
81 CounterTimer
=0x20410500,
82 Timer100Ns
=0x20510500,
83 SampleFraction
=0x20C20400,
84 CounterTimerInverse
=0x21410500,
85 Timer100NsInverse
=0x21510500,
86 CounterMultiTimer
=0x22410500,
87 CounterMultiTimer100Ns
=0x22510500,
88 CounterMultiTimerInverse
=0x23410500,
89 CounterMultiTimer100NsInverse
=0x23510500,
90 AverageTimer32
=0x30020400,
91 ElapsedTime
=0x30240500,
92 AverageCount64
=0x40020500,
93 SampleBase
=0x40030401,
94 AverageBase
=0x40030402,
96 CounterMultiBase
=0x42030500
99 /* maps a small integer type to the counter types above */
101 simple_type_to_type
[] = {
102 NumberOfItemsHEX32
, NumberOfItemsHEX64
,
103 NumberOfItems32
, NumberOfItems64
,
104 CounterDelta32
, CounterDelta64
,
105 SampleCounter
, CountPerTimeInterval32
,
106 CountPerTimeInterval64
, RateOfCountsPerSecond32
,
107 RateOfCountsPerSecond64
, RawFraction
,
108 CounterTimer
, Timer100Ns
,
109 SampleFraction
, CounterTimerInverse
,
110 Timer100NsInverse
, CounterMultiTimer
,
111 CounterMultiTimer100Ns
, CounterMultiTimerInverse
,
112 CounterMultiTimer100NsInverse
, AverageTimer32
,
113 ElapsedTime
, AverageCount64
,
114 SampleBase
, AverageBase
,
115 RawBase
, CounterMultiBase
129 NetworkInterfaceInstance
,
133 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
134 #define PERFCTR_COUNTER(id,name,help,type,field)
136 #include "mono-perfcounters-def.h"
141 #undef PERFCTR_COUNTER
142 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
143 #define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
144 /* each counter is assigned an id starting from 0 inside the category */
146 #include "mono-perfcounters-def.h"
151 #undef PERFCTR_COUNTER
152 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
153 #define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
154 /* this is used just to count the number of counters */
156 #include "mono-perfcounters-def.h"
160 static mono_mutex_t perfctr_mutex
;
161 #define perfctr_lock() mono_os_mutex_lock (&perfctr_mutex)
162 #define perfctr_unlock() mono_os_mutex_unlock (&perfctr_mutex)
167 unsigned short counters_start
;
168 unsigned short counters_size
;
169 unsigned short data_start
;
170 MonoPerfCounters counters
;
175 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
177 struct stanza_header {
178 byte stanza_type; // FTYPE_*
180 ushort stanza_length; // includeas header
185 // perfcat and perfinstance are 4-bytes aligned
189 ushort length; // includes the counters
191 ushort counters_data_size;
193 char name[]; // null terminated
194 char help[]; // null terminated
195 // perfcounters follow
198 char name[]; // null terminated
199 char help[]; // null terminated
204 struct perfinstance {
206 byte data_offset; // offset of counters from beginning of struct
208 uint category_offset; // offset of category in the shared area
209 char name[]; // null terminated
210 // data follows: this is always 8-byte aligned
216 FTYPE_CATEGORY
= 'C',
218 FTYPE_PREDEF_INSTANCE
= 'P', // an instance of a predef counter
219 FTYPE_INSTANCE
= 'I',
232 unsigned short num_counters
;
233 unsigned short counters_data_size
;
235 /* variable length data follows */
239 // SharedCounter counters_info [num_counters]
244 unsigned int category_offset
;
245 /* variable length data follows */
246 char instance_name
[1];
253 /* variable length data follows */
266 unsigned int instance_type
: 6;
276 unsigned short offset
; // offset inside MonoPerfCounters
281 #undef PERFCTR_COUNTER
282 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, sizeof (name) - 1, sizeof (help) - 1, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
283 #define PERFCTR_COUNTER(id,name,help,type,field)
284 static const CategoryDesc
285 predef_categories
[] = {
286 #include "mono-perfcounters-def.h"
287 {NULL
, NULL
, 0, 0, NUM_CATEGORIES
, -1, 0, NUM_COUNTERS
}
291 #undef PERFCTR_COUNTER
292 #define PERFCTR_CAT(id,name,help,type,inst,first_counter)
293 #define PERFCTR_COUNTER(id,name,help,type,field) {name, help, sizeof (name) - 1, sizeof (help) - 1, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
294 static const CounterDesc
295 predef_counters
[] = {
296 #include "mono-perfcounters-def.h"
297 {NULL
, NULL
, 0, 0, -1, 0, 0}
301 * We have several different classes of counters:
303 * *) runtime counters
305 * *) user-defined counters
306 * *) windows counters (the implementation on windows will use this)
308 * To easily handle the differences we create a vtable for each class that contains the
309 * function pointers with the actual implementation to access the counters.
311 typedef struct _ImplVtable ImplVtable
;
313 typedef MonoBoolean (*SampleFunc
) (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
* sample
);
314 typedef gint64 (*UpdateFunc
) (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
);
315 typedef void (*CleanupFunc
) (ImplVtable
*vtable
);
331 MonoPerfCounters
*counters
;
337 SharedInstance
*instance_desc
;
338 SharedCounter
*counter_desc
;
342 create_vtable (void *arg
, SampleFunc sample
, UpdateFunc update
)
344 ImplVtable
*vtable
= g_new0 (ImplVtable
, 1);
346 vtable
->sample
= sample
;
347 vtable
->update
= update
;
351 MonoPerfCounters
*mono_perfcounters
= NULL
;
352 static MonoSharedArea
*shared_area
= NULL
;
359 /* maps a pid to a ExternalSArea pointer */
360 static GHashTable
*pid_to_shared_area
= NULL
;
362 static MonoSharedArea
*
363 load_sarea_for_pid (int pid
)
366 MonoSharedArea
*area
= NULL
;
369 if (pid_to_shared_area
== NULL
)
370 pid_to_shared_area
= g_hash_table_new (NULL
, NULL
);
371 data
= (ExternalSArea
*)g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
373 area
= (MonoSharedArea
*)mono_shared_area_for_pid (GINT_TO_POINTER (pid
));
375 data
= g_new (ExternalSArea
, 1);
378 g_hash_table_insert (pid_to_shared_area
, GINT_TO_POINTER (pid
), data
);
381 area
= (MonoSharedArea
*)data
->sarea
;
389 unref_pid_unlocked (int pid
)
392 data
= (ExternalSArea
*)g_hash_table_lookup (pid_to_shared_area
, GINT_TO_POINTER (pid
));
395 if (!data
->refcount
) {
396 g_hash_table_remove (pid_to_shared_area
, GINT_TO_POINTER (pid
));
397 mono_shared_area_unload (data
->sarea
);
404 predef_cleanup (ImplVtable
*vtable
)
406 PredefVtable
*vt
= (PredefVtable
*)vtable
;
407 /* ExternalSArea *data; */
410 if (!pid_to_shared_area
) {
414 unref_pid_unlocked (vt
->pid
);
419 mono_determine_physical_ram_size (void)
421 #if defined (TARGET_WIN32)
422 MEMORYSTATUSEX memstat
;
424 memstat
.dwLength
= sizeof (memstat
);
425 GlobalMemoryStatusEx (&memstat
);
426 return (guint64
)memstat
.ullTotalPhys
;
427 #elif defined (__NetBSD__) || defined (__APPLE__)
441 size_t size_sys
= sizeof (value
);
443 sysctl (mib
, 2, &value
, &size_sys
, NULL
, 0);
447 return (guint64
)value
;
448 #elif defined (HAVE_SYSCONF)
449 guint64 page_size
= 0, num_pages
= 0;
451 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
452 * reports invalid values, please add your OS specific code below. */
454 page_size
= (guint64
)sysconf (_SC_PAGESIZE
);
457 #ifdef _SC_PHYS_PAGES
458 num_pages
= (guint64
)sysconf (_SC_PHYS_PAGES
);
461 if (!page_size
|| !num_pages
) {
462 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
466 return page_size
* num_pages
;
473 mono_determine_physical_ram_available_size (void)
475 #if defined (TARGET_WIN32)
476 MEMORYSTATUSEX memstat
;
478 memstat
.dwLength
= sizeof (memstat
);
479 GlobalMemoryStatusEx (&memstat
);
480 return (guint64
)memstat
.ullAvailPhys
;
482 #elif defined (__NetBSD__)
483 struct vmtotal vm_total
;
491 len
= sizeof (vm_total
);
492 sysctl (mib
, 2, &vm_total
, &len
, NULL
, 0);
495 mib
[1] = HW_PAGESIZE
;
497 len
= sizeof (page_size
);
498 sysctl (mib
, 2, &page_size
, &len
, NULL
, 0);
500 return ((guint64
) vm_total
.t_free
* page_size
) / 1024;
501 #elif defined (__APPLE__)
502 mach_msg_type_number_t count
= HOST_VM_INFO_COUNT
;
503 mach_port_t host
= mach_host_self();
505 vm_statistics_data_t vmstat
;
508 ret
= host_statistics(host
, HOST_VM_INFO
, (host_info_t
)&vmstat
, &count
);
509 } while (ret
== KERN_ABORTED
);
511 if (ret
!= KERN_SUCCESS
) {
512 g_warning ("Mono was unable to retrieve memory usage!");
516 host_page_size(host
, &page_size
);
517 return (guint64
) vmstat
.free_count
* page_size
;
519 #elif defined (HAVE_SYSCONF)
520 guint64 page_size
= 0, num_pages
= 0;
522 /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
523 * reports invalid values, please add your OS specific code below. */
525 page_size
= (guint64
)sysconf (_SC_PAGESIZE
);
528 #ifdef _SC_AVPHYS_PAGES
529 num_pages
= (guint64
)sysconf (_SC_AVPHYS_PAGES
);
532 if (!page_size
|| !num_pages
) {
533 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
537 return page_size
* num_pages
;
544 mono_perfcounters_init (void)
546 int d_offset
= G_STRUCT_OFFSET (MonoSharedArea
, data
);
550 mono_os_mutex_init_recursive (&perfctr_mutex
);
552 shared_area
= (MonoSharedArea
*)mono_shared_area ();
553 shared_area
->counters_start
= G_STRUCT_OFFSET (MonoSharedArea
, counters
);
554 shared_area
->counters_size
= sizeof (MonoPerfCounters
);
555 shared_area
->data_start
= d_offset
;
556 shared_area
->size
= 4096;
557 mono_perfcounters
= &shared_area
->counters
;
561 perfctr_type_compress (int type
)
564 for (i
= 0; i
< G_N_ELEMENTS (simple_type_to_type
); ++i
) {
565 if (simple_type_to_type
[i
] == type
)
568 /* NumberOfItems32 */
573 shared_data_reserve_room (int size
, int ftype
)
575 SharedHeader
* header
;
576 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
577 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
582 unsigned short *next
;
583 if (*p
== FTYPE_END
) {
584 if (size
< (end
- p
))
590 next
= (unsigned short*)(p
+ 2);
591 if (*p
== FTYPE_DELETED
) {
592 /* we reuse only if it's the same size */
602 header
= (SharedHeader
*)p
;
603 header
->ftype
= ftype
;
604 header
->extra
= 0; /* data_offset could overflow here, so we leave this field unused */
610 typedef gboolean (*SharedFunc
) (SharedHeader
*header
, void *data
);
613 foreach_shared_item_in_area (unsigned char *p
, unsigned char *end
, SharedFunc func
, void *data
)
616 unsigned short *next
;
619 next
= (unsigned short*)(p
+ 2);
620 if (!func ((SharedHeader
*)p
, data
))
629 foreach_shared_item (SharedFunc func
, void *data
)
631 unsigned char *p
= (unsigned char *)shared_area
+ shared_area
->data_start
;
632 unsigned char *end
= (unsigned char *)shared_area
+ shared_area
->size
;
634 foreach_shared_item_in_area (p
, end
, func
, data
);
638 mono_utf16_equal_ascii (const gunichar2
*strc
, const char *ascii_str
)
640 /* FIXME: make this case insensitive */
641 while (*strc
== *ascii_str
++) {
646 return *strc
== *(const unsigned char *)(ascii_str
- 1);
650 mono_utf16_equal_ascii_len (const gunichar2
*utf16
, int utf16_length
, const char *ascii
, int ascii_length
)
652 /* FIXME: make this case insensitive */
653 if (utf16_length
!= ascii_length
)
655 for (int i
= 0; i
< utf16_length
; ++i
) {
656 if (ascii
[i
] != utf16
[i
])
663 const gunichar2
*name
;
668 category_search (SharedHeader
*header
, void *data
)
670 CatSearch
*search
= (CatSearch
*)data
;
671 if (header
->ftype
== FTYPE_CATEGORY
) {
672 SharedCategory
*cat
= (SharedCategory
*)header
;
673 if (mono_utf16_equal_ascii (search
->name
, cat
->name
)) {
681 static SharedCategory
*
682 find_custom_category (const gunichar2
*name
)
687 foreach_shared_item (category_search
, &search
);
692 category_collect (SharedHeader
*header
, void *data
)
694 GSList
**list
= (GSList
**)data
;
695 if (header
->ftype
== FTYPE_CATEGORY
) {
696 *list
= g_slist_prepend (*list
, header
);
702 get_custom_categories (void) {
704 foreach_shared_item (category_collect
, &list
);
708 static SharedCounter
*
709 custom_category_counters (SharedCategory
* cat
)
711 char *help
= cat
->name
+ strlen (cat
->name
) + 1;
712 return (SharedCounter
*)(help
+ strlen (help
) + 1);
715 static SharedCounter
*
716 next_custom_category_counter (SharedCounter
* counter
)
718 char *help
= counter
->name
+ strlen (counter
->name
) + 1;
719 return (SharedCounter
*)(help
+ strlen (help
) + 1);
722 static SharedCounter
*
723 find_custom_counter (SharedCategory
* cat
, const gunichar2
*name
)
726 SharedCounter
*counter
= custom_category_counters (cat
);
727 for (i
= 0; i
< cat
->num_counters
; ++i
) {
728 if (mono_utf16_equal_ascii (name
, counter
->name
))
730 counter
= next_custom_category_counter (counter
);
736 unsigned int cat_offset
;
739 SharedInstance
* result
;
744 instance_search (SharedHeader
*header
, void *data
)
746 InstanceSearch
*search
= (InstanceSearch
*)data
;
747 if (header
->ftype
== FTYPE_INSTANCE
) {
748 SharedInstance
*ins
= (SharedInstance
*)header
;
749 if (search
->cat_offset
== ins
->category_offset
) {
751 if (strcmp (search
->name
, ins
->instance_name
) == 0) {
752 search
->result
= ins
;
756 search
->list
= g_slist_prepend (search
->list
, ins
);
763 static SharedInstance
*
764 find_custom_instance (SharedCategory
* cat
, char *name
)
766 InstanceSearch search
;
767 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
771 search
.result
= NULL
;
772 foreach_shared_item (instance_search
, &search
);
773 return search
.result
;
777 get_custom_instances_list (SharedCategory
* cat
)
779 InstanceSearch search
;
780 search
.cat_offset
= (char*)cat
- (char*)shared_area
;
784 search
.result
= NULL
;
785 foreach_shared_item (instance_search
, &search
);
790 custom_category_help (SharedCategory
* cat
)
792 return cat
->name
+ strlen (cat
->name
) + 1;
795 static const CounterDesc
*
796 get_counter_in_category (const CategoryDesc
*desc
, const gunichar2
*counter
, int counter_length
)
798 const CounterDesc
*cdesc
= &predef_counters
[desc
->first_counter
];
799 const CounterDesc
*end
= &predef_counters
[desc
[1].first_counter
];
800 for (; cdesc
< end
; ++cdesc
) {
801 if (mono_utf16_equal_ascii_len (counter
, counter_length
, cdesc
->name
, cdesc
->name_length
))
807 /* fill the info in sample (except the raw value) */
809 fill_sample (MonoCounterSample
*sample
)
811 sample
->timeStamp
= mono_100ns_ticks ();
812 sample
->timeStamp100nSec
= sample
->timeStamp
;
813 sample
->counterTimeStamp
= sample
->timeStamp
;
814 sample
->counterFrequency
= 10000000;
815 sample
->systemFrequency
= 10000000;
816 // the real basevalue needs to be get from a different counter...
817 sample
->baseValue
= 0;
821 id_from_string (const gchar
*id_str
, gboolean is_process
)
824 if (strcmp("", id_str
) != 0) {
826 id
= strtol (id_str
, &end
, 0);
827 if (end
== id_str
&& !is_process
)
834 get_cpu_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
836 MonoProcessError error
;
837 int id
= GPOINTER_TO_INT (vtable
->arg
);
841 fill_sample (sample
);
842 sample
->baseValue
= 1;
844 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_CPU
].first_counter
+ id
].type
;
846 case COUNTER_CPU_USER_TIME
:
847 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_USER_TIME
, &error
);
849 case COUNTER_CPU_PRIV_TIME
:
850 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_PRIV_TIME
, &error
);
852 case COUNTER_CPU_INTR_TIME
:
853 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_INTR_TIME
, &error
);
855 case COUNTER_CPU_DCP_TIME
:
856 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_DCP_TIME
, &error
);
858 case COUNTER_CPU_PROC_TIME
:
859 sample
->rawValue
= mono_cpu_get_data (pid
, MONO_CPU_IDLE_TIME
, &error
);
866 cpu_get_impl (const gunichar2
*counter
, int counter_length
, const char *instance
, int *type
, MonoBoolean
*custom
)
868 int id
= id_from_string (instance
, FALSE
) << 5;
869 const CounterDesc
*cdesc
;
871 /* increase the shift above and the mask also in the implementation functions */
872 //g_assert (32 > desc [1].first_counter - desc->first_counter);
873 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_CPU
], counter
, counter_length
))) {
875 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_cpu_counter
, NULL
);
881 get_network_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
883 MonoNetworkError error
= MONO_NETWORK_ERROR_OTHER
;
884 NetworkVtableArg
*narg
= (NetworkVtableArg
*) vtable
->arg
;
886 fill_sample (sample
);
889 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_NETWORK
].first_counter
+ narg
->id
].type
;
891 case COUNTER_NETWORK_BYTESRECSEC
:
892 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESREC
, &error
);
894 case COUNTER_NETWORK_BYTESSENTSEC
:
895 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESSENT
, &error
);
897 case COUNTER_NETWORK_BYTESTOTALSEC
:
898 sample
->rawValue
= mono_network_get_data (narg
->name
, MONO_NETWORK_BYTESTOTAL
, &error
);
902 if (error
== MONO_NETWORK_ERROR_NONE
)
909 network_cleanup (ImplVtable
*vtable
)
911 NetworkVtableArg
*narg
;
916 narg
= (NetworkVtableArg
*)vtable
->arg
;
927 network_get_impl (const gunichar2
*counter
, int counter_length
, const char *instance
, int *type
, MonoBoolean
*custom
)
929 const CounterDesc
*cdesc
;
930 NetworkVtableArg
*narg
;
935 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_NETWORK
], counter
, counter_length
))) {
936 instance_name
= g_strdup (instance
);
937 narg
= g_new0 (NetworkVtableArg
, 1);
938 narg
->id
= cdesc
->id
;
939 narg
->name
= instance_name
;
941 vtable
= create_vtable (narg
, get_network_counter
, NULL
);
942 vtable
->cleanup
= network_cleanup
;
949 get_process_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
951 int id
= GPOINTER_TO_INT (vtable
->arg
);
957 fill_sample (sample
);
958 sample
->baseValue
= 1;
960 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_PROC
].first_counter
+ id
].type
;
962 case COUNTER_PROC_USER_TIME
:
963 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_USER_TIME
);
965 case COUNTER_PROC_PRIV_TIME
:
966 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_SYSTEM_TIME
);
968 case COUNTER_PROC_PROC_TIME
:
969 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_TOTAL_TIME
);
971 case COUNTER_PROC_THREADS
:
972 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_NUM_THREADS
);
974 case COUNTER_PROC_VBYTES
:
975 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_VIRTUAL_BYTES
);
977 case COUNTER_PROC_WSET
:
978 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_WORKING_SET
);
980 case COUNTER_PROC_PBYTES
:
981 sample
->rawValue
= mono_process_get_data (GINT_TO_POINTER (pid
), MONO_PROCESS_PRIVATE_BYTES
);
988 process_get_impl (const gunichar2
*counter
, int counter_length
, const char *instance
, int *type
, MonoBoolean
*custom
)
990 int id
= id_from_string (instance
, TRUE
) << 5;
991 const CounterDesc
*cdesc
;
993 /* increase the shift above and the mask also in the implementation functions */
994 //g_assert (32 > desc [1].first_counter - desc->first_counter);
995 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_PROC
], counter
, counter_length
))) {
997 return create_vtable (GINT_TO_POINTER (id
| cdesc
->id
), get_process_counter
, NULL
);
1003 mono_mem_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1005 int id
= GPOINTER_TO_INT (vtable
->arg
);
1007 fill_sample (sample
);
1008 sample
->baseValue
= 1;
1010 sample
->counterType
= predef_counters
[predef_categories
[CATEGORY_MONO_MEM
].first_counter
+ id
].type
;
1012 case COUNTER_MEM_NUM_OBJECTS
:
1013 sample
->rawValue
= 0;
1015 case COUNTER_MEM_PHYS_TOTAL
:
1016 sample
->rawValue
= mono_determine_physical_ram_size ();
1018 case COUNTER_MEM_PHYS_AVAILABLE
:
1019 sample
->rawValue
= mono_determine_physical_ram_available_size ();
1026 mono_mem_get_impl (const gunichar2
*counter
, int counter_length
, const char *instance
, int *type
, MonoBoolean
*custom
)
1028 const CounterDesc
*cdesc
;
1030 if ((cdesc
= get_counter_in_category (&predef_categories
[CATEGORY_MONO_MEM
], counter
, counter_length
))) {
1031 *type
= cdesc
->type
;
1032 return create_vtable (GINT_TO_POINTER ((gint
) cdesc
->id
), mono_mem_counter
, NULL
);
1038 predef_readonly_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1040 PredefVtable
*vt
= (PredefVtable
*)vtable
;
1041 const CounterDesc
*desc
;
1042 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
1043 int id
= cat_id
>> 16;
1046 fill_sample (sample
);
1047 sample
->baseValue
= 1;
1049 desc
= &predef_counters
[predef_categories
[cat_id
].first_counter
+ id
];
1050 sample
->counterType
= desc
->type
;
1051 /* FIXME: check that the offset fits inside imported counters */
1052 /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
1053 sample
->rawValue
= *(guint32
*)((char*)vt
->counters
+ desc
->offset
);
1058 predef_vtable (void *arg
, const gchar
*pids
)
1060 MonoSharedArea
*area
;
1061 PredefVtable
*vtable
;
1065 area
= load_sarea_for_pid (pid
);
1069 vtable
= g_new (PredefVtable
, 1);
1070 vtable
->vtable
.arg
= arg
;
1071 vtable
->vtable
.sample
= predef_readonly_counter
;
1072 vtable
->vtable
.cleanup
= predef_cleanup
;
1073 vtable
->counters
= (MonoPerfCounters
*)((char*)area
+ area
->counters_start
);
1076 return (ImplVtable
*)vtable
;
1079 /* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
1080 * this needs some way to set sample->counterType as well, though.
1083 predef_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1085 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
1086 int id
= cat_id
>> 16;
1089 fill_sample (sample
);
1090 sample
->baseValue
= 1;
1092 sample
->counterType
= predef_counters
[predef_categories
[cat_id
].first_counter
+ id
].type
;
1096 case COUNTER_EXC_THROWN
:
1097 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->exceptions_thrown
);
1101 case CATEGORY_ASPNET
:
1103 case COUNTER_ASPNET_REQ_Q
:
1104 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->aspnet_requests_queued
);
1106 case COUNTER_ASPNET_REQ_TOTAL
:
1107 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->aspnet_requests
);
1111 case CATEGORY_THREADPOOL
:
1113 case COUNTER_THREADPOOL_WORKITEMS
:
1114 sample
->rawValue
= mono_atomic_load_i64 (&mono_perfcounters
->threadpool_workitems
);
1116 case COUNTER_THREADPOOL_IOWORKITEMS
:
1117 sample
->rawValue
= mono_atomic_load_i64 (&mono_perfcounters
->threadpool_ioworkitems
);
1119 case COUNTER_THREADPOOL_THREADS
:
1120 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->threadpool_threads
);
1122 case COUNTER_THREADPOOL_IOTHREADS
:
1123 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->threadpool_iothreads
);
1129 case COUNTER_JIT_BYTES
:
1130 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->jit_bytes
);
1132 case COUNTER_JIT_METHODS
:
1133 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->jit_methods
);
1135 case COUNTER_JIT_TIME
:
1136 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->jit_time
);
1138 case COUNTER_JIT_BYTES_PSEC
:
1139 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->jit_bytes
);
1141 case COUNTER_JIT_FAILURES
:
1142 sample
->rawValue
= mono_atomic_load_i32 (&mono_perfcounters
->jit_failures
);
1151 predef_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
1153 gint32
*volatile ptr
= NULL
;
1154 gint64
*volatile ptr64
= NULL
;
1155 int cat_id
= GPOINTER_TO_INT (vtable
->arg
);
1156 int id
= cat_id
>> 16;
1159 case CATEGORY_ASPNET
:
1161 case COUNTER_ASPNET_REQ_Q
: ptr
= &mono_perfcounters
->aspnet_requests_queued
; break;
1162 case COUNTER_ASPNET_REQ_TOTAL
: ptr
= &mono_perfcounters
->aspnet_requests
; break;
1165 case CATEGORY_THREADPOOL
:
1167 case COUNTER_THREADPOOL_WORKITEMS
: ptr64
= &mono_perfcounters
->threadpool_workitems
; break;
1168 case COUNTER_THREADPOOL_IOWORKITEMS
: ptr64
= &mono_perfcounters
->threadpool_ioworkitems
; break;
1169 case COUNTER_THREADPOOL_THREADS
: ptr
= &mono_perfcounters
->threadpool_threads
; break;
1170 case COUNTER_THREADPOOL_IOTHREADS
: ptr
= &mono_perfcounters
->threadpool_iothreads
; break;
1177 return mono_atomic_inc_i32 (ptr
);
1179 return mono_atomic_dec_i32 (ptr
);
1181 return mono_atomic_add_i32 (ptr
, (gint32
)value
);
1183 /* this can be non-atomic */
1189 return UnlockedIncrement64 (ptr64
); /* FIXME: use mono_atomic_inc_i64 () */
1191 return UnlockedDecrement64 (ptr64
); /* FIXME: use mono_atomic_dec_i64 () */
1193 return UnlockedAdd64 (ptr64
, value
); /* FIXME: use mono_atomic_add_i64 () */
1195 /* this can be non-atomic */
1203 predef_writable_get_impl (int cat
, const gunichar2
*counter
, int counter_length
, const char *instance
, int *type
, MonoBoolean
*custom
)
1205 const CounterDesc
*cdesc
;
1207 if ((cdesc
= get_counter_in_category (&predef_categories
[cat
], counter
, counter_length
))) {
1208 *type
= cdesc
->type
;
1209 if (instance
== NULL
|| strcmp (instance
, "") == 0)
1210 return create_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), predef_writable_counter
, predef_writable_update
);
1212 return predef_vtable (GINT_TO_POINTER ((cdesc
->id
<< 16) | cat
), instance
);
1218 custom_writable_counter (ImplVtable
*vtable
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1220 CustomVTable
*counter_data
= (CustomVTable
*)vtable
;
1222 fill_sample (sample
);
1223 sample
->baseValue
= 1;
1225 sample
->counterType
= simple_type_to_type
[counter_data
->counter_desc
->type
];
1227 sample
->rawValue
= 0;
1229 sample
->rawValue
= *(guint64
*)vtable
->arg
;
1234 custom_writable_update (ImplVtable
*vtable
, MonoBoolean do_incr
, gint64 value
)
1236 /* FIXME: check writability */
1237 guint64
*ptr
= (guint64
*)vtable
->arg
;
1240 /* FIXME: we need to do this atomically */
1244 /* this can be non-atomic */
1251 static SharedInstance
*
1252 custom_get_instance (SharedCategory
*cat
, SharedCounter
*scounter
, char* name
)
1254 SharedInstance
* inst
;
1257 inst
= find_custom_instance (cat
, name
);
1260 size
= sizeof (SharedInstance
) + strlen (name
);
1263 size
+= (sizeof (guint64
) * cat
->num_counters
);
1265 inst
= (SharedInstance
*) shared_data_reserve_room (size
, FTYPE_INSTANCE
);
1270 inst
->category_offset
= (char*)cat
- (char*)shared_area
;
1271 cat
->num_instances
++;
1272 /* now copy the variable data */
1273 p
= inst
->instance_name
;
1275 p
+= strlen (name
) + 1;
1282 custom_vtable (SharedCounter
*scounter
, SharedInstance
* inst
, char *data
)
1284 CustomVTable
* vtable
;
1285 vtable
= g_new0 (CustomVTable
, 1);
1286 vtable
->vtable
.arg
= data
;
1287 vtable
->vtable
.sample
= custom_writable_counter
;
1288 vtable
->vtable
.update
= custom_writable_update
;
1289 vtable
->instance_desc
= inst
;
1290 vtable
->counter_desc
= scounter
;
1292 return (ImplVtable
*)vtable
;
1296 custom_get_value_address (SharedCounter
*scounter
, SharedInstance
* sinst
)
1298 int offset
= sizeof (SharedInstance
) + strlen (sinst
->instance_name
);
1301 offset
+= scounter
->seq_num
* sizeof (guint64
);
1302 return (char*)sinst
+ offset
;
1306 custom_get_impl (SharedCategory
*cat
, const gunichar2
*counter
, int counter_length
, const gunichar2
*instance
, int *type
, MonoError
*error
)
1308 SharedCounter
*scounter
;
1309 SharedInstance
* inst
;
1313 scounter
= find_custom_counter (cat
, counter
);
1316 name
= mono_utf16_to_utf8 (counter
, counter_length
, error
);
1317 return_val_if_nok (error
, NULL
);
1318 *type
= simple_type_to_type
[scounter
->type
];
1319 inst
= custom_get_instance (cat
, scounter
, name
);
1323 return custom_vtable (scounter
, inst
, (char *)custom_get_value_address (scounter
, inst
));
1326 static const CategoryDesc
*
1327 find_category (const gunichar2
*category
, int category_length
)
1330 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1331 if (mono_utf16_equal_ascii_len (category
, category_length
, predef_categories
[i
].name
, predef_categories
[i
].name_length
))
1332 return &predef_categories
[i
];
1338 mono_perfcounter_get_impl (const gunichar2
*category
, gint32 category_length
,
1339 const gunichar2
*counter
, gint32 counter_length
,
1340 const gunichar2
*instance
, gint32 instance_length
,
1341 gint32
*type
, MonoBoolean
*custom
, MonoError
*error
)
1343 const CategoryDesc
*cdesc
;
1344 void *result
= NULL
;
1345 cdesc
= find_category (category
, category_length
);
1347 SharedCategory
*scat
= find_custom_category (category
);
1351 result
= custom_get_impl (scat
, counter
, counter_length
, instance
, type
, error
);
1352 return_val_if_nok (error
, NULL
);
1355 char *c_instance
= mono_utf16_to_utf8 (instance
, instance_length
, error
);
1356 return_val_if_nok (error
, NULL
);
1357 switch (cdesc
->id
) {
1359 result
= cpu_get_impl (counter
, counter_length
, c_instance
, type
, custom
);
1362 result
= process_get_impl (counter
, counter_length
, c_instance
, type
, custom
);
1364 case CATEGORY_MONO_MEM
:
1365 result
= mono_mem_get_impl (counter
, counter_length
, c_instance
, type
, custom
);
1367 case CATEGORY_NETWORK
:
1368 result
= network_get_impl (counter
, counter_length
, c_instance
, type
, custom
);
1373 case CATEGORY_REMOTING
:
1374 case CATEGORY_LOADING
:
1375 case CATEGORY_THREAD
:
1376 case CATEGORY_INTEROP
:
1377 case CATEGORY_SECURITY
:
1378 case CATEGORY_ASPNET
:
1379 case CATEGORY_THREADPOOL
:
1380 result
= predef_writable_get_impl (cdesc
->id
, counter
, counter_length
, c_instance
, type
, custom
);
1383 g_free (c_instance
);
1388 mono_perfcounter_get_sample (void *impl
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1390 ImplVtable
*vtable
= (ImplVtable
*)impl
;
1391 if (vtable
&& vtable
->sample
)
1392 return vtable
->sample (vtable
, only_value
, sample
);
1397 mono_perfcounter_update_value (void *impl
, MonoBoolean do_incr
, gint64 value
)
1399 ImplVtable
*vtable
= (ImplVtable
*)impl
;
1400 if (vtable
&& vtable
->update
)
1401 return vtable
->update (vtable
, do_incr
, value
);
1406 mono_perfcounter_free_data (void *impl
)
1408 ImplVtable
*vtable
= (ImplVtable
*)impl
;
1409 if (vtable
&& vtable
->cleanup
)
1410 vtable
->cleanup (vtable
);
1414 /* Category icalls */
1416 mono_perfcounter_category_del (const gunichar2
*name
, gint32 name_length
, MonoError
*error
)
1418 const CategoryDesc
*cdesc
;
1419 SharedCategory
*cat
;
1420 cdesc
= find_category (name
, name_length
);
1421 /* can't delete a predefined category */
1425 cat
= find_custom_category (name
);
1426 /* FIXME: check the semantics, if deleting a category means also deleting the instances */
1427 if (!cat
|| cat
->num_instances
) {
1431 cat
->header
.ftype
= FTYPE_DELETED
;
1436 /* this is an icall */
1438 mono_perfcounter_category_help (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
1440 MonoStringHandle result
= NULL_HANDLE_STRING
;
1441 const CategoryDesc
*cdesc
;
1443 cdesc
= find_category (category
, category_length
);
1445 SharedCategory
*scat
= find_custom_category (category
);
1447 return NULL_HANDLE_STRING
;
1448 result
= mono_string_new_handle (mono_domain_get (), custom_category_help (scat
), error
);
1449 return_val_if_nok (error
, NULL_HANDLE_STRING
);
1452 result
= mono_string_new_utf8_len (mono_domain_get (), cdesc
->help
, cdesc
->help_length
, error
);
1453 return_val_if_nok (error
, NULL_HANDLE_STRING
);
1458 * Check if the category named @category exists. If @counter is not NULL, return
1459 * TRUE only if a counter with that name exists in the category.
1462 mono_perfcounter_category_exists (const gunichar2
*counter
, gint32 counter_length
, const gunichar2
*category
,
1463 gint32 category_length
, MonoError
*error
)
1465 const CategoryDesc
*cdesc
;
1466 cdesc
= find_category (category
, category_length
);
1468 SharedCategory
*scat
= find_custom_category (category
);
1471 /* counter is allowed to be null */
1474 /* search through the custom category */
1475 return find_custom_counter (scat
, counter
) != NULL
;
1477 /* counter is allowed to be null */
1480 if (get_counter_in_category (cdesc
, counter
, counter_length
))
1485 /* C map of the type with the same name */
1491 } CounterCreationData
;
1493 TYPED_HANDLE_DECL (CounterCreationData
);
1496 * Since we'll keep a copy of the category per-process, we should also make sure
1497 * categories with the same name are compatible.
1500 mono_perfcounter_create (const gunichar2
*category
, gint32 category_length
, const gunichar2
*help
,
1501 gint32 help_length
, gint32 type
, MonoArrayHandle items
, MonoError
*error
)
1504 int i
, j
= 0, k
, size
;
1505 int num_counters
= mono_array_handle_length (items
);
1506 int counters_data_size
;
1509 char **counter_info
= NULL
;
1511 SharedCategory
*cat
;
1512 gsize name_length
= 0;
1513 gsize chelp_length
= 0;
1514 CounterCreationDataHandle data
= MONO_HANDLE_NEW (CounterCreationData
, NULL
);
1515 MonoStringHandle str
= MONO_HANDLE_NEW (MonoString
, NULL
);
1517 /* FIXME: ensure there isn't a category created already */
1518 name
= mono_utf16_to_utf8len (category
, category_length
, &name_length
, error
);
1519 goto_if_nok (error
, failure
);
1520 chelp
= mono_utf16_to_utf8len (help
, help_length
, &chelp_length
, error
);
1521 goto_if_nok (error
, failure
);
1522 counter_info
= g_new0 (char*, num_counters
* 2);
1523 /* calculate the size we need structure size + name/help + 2 0 string terminators */
1524 size
= G_STRUCT_OFFSET (SharedCategory
, name
) + name_length
+ chelp_length
+ 2;
1525 for (i
= 0; i
< num_counters
; ++i
) {
1526 MONO_HANDLE_ARRAY_GETREF (data
, items
, i
);
1527 for (k
= 0; k
< 2; ++k
) {
1529 MONO_HANDLE_GET (str
, data
, help
);
1531 MONO_HANDLE_GET (str
, data
, name
);
1532 gsize utf8_length
= 0;
1533 counter_info
[j
] = mono_string_to_utf8len (str
, &utf8_length
, error
);
1534 if (!is_ok (error
) || !counter_info
[j
])
1536 size
+= utf8_length
+ 1;
1539 size
+= sizeof (SharedCounter
) + 1; /* FIXME +1 should be -1 */
1543 counters_data_size
= num_counters
* 8; /* optimize for size later */
1547 cat
= (SharedCategory
*) shared_data_reserve_room (size
, FTYPE_CATEGORY
);
1552 cat
->num_counters
= num_counters
;
1553 cat
->counters_data_size
= counters_data_size
;
1554 /* now copy the vaiable data */
1557 p
+= strlen (name
) + 1;
1559 p
+= strlen (chelp
) + 1;
1561 for (i
= 0; i
< num_counters
; ++i
) {
1562 MONO_HANDLE_ARRAY_GETREF (data
, items
, i
);
1563 /* emit the SharedCounter structures */
1564 *p
++ = perfctr_type_compress (MONO_HANDLE_GETVAL (data
, type
));
1566 strcpy (p
, counter_info
[i
* 2]);
1567 p
+= strlen (counter_info
[i
* 2]) + 1;
1568 strcpy (p
, counter_info
[i
* 2 + 1]);
1569 p
+= strlen (counter_info
[i
* 2 + 1]) + 1;
1576 for (i
= 0; i
< num_counters
* 2; ++i
) {
1577 g_free (counter_info
[i
]);
1579 g_free (counter_info
);
1587 mono_perfcounter_instance_exists (const gunichar2
*instance
, gint32 instance_length
,
1588 const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
1590 const CategoryDesc
*cdesc
;
1591 SharedInstance
*sinst
;
1593 cdesc
= find_category (category
, category_length
);
1595 SharedCategory
*scat
;
1596 scat
= find_custom_category (category
);
1599 name
= mono_utf16_to_utf8 (instance
, instance_length
, error
);
1600 return_val_if_nok (error
, FALSE
);
1601 sinst
= find_custom_instance (scat
, name
);
1606 /* FIXME: search instance */
1611 /* this is an icall */
1613 mono_perfcounter_category_names (MonoError
*error
)
1615 HANDLE_LOOP_PREPARE
;
1618 MonoArrayHandle res
;
1619 MonoDomain
*domain
= mono_domain_get ();
1620 GSList
*custom_categories
, *tmp
;
1622 custom_categories
= get_custom_categories ();
1623 res
= mono_array_new_handle (domain
, mono_get_string_class (), NUM_CATEGORIES
+ g_slist_length (custom_categories
), error
);
1624 if (!is_ok (error
)) {
1625 res
= NULL_HANDLE_ARRAY
;
1629 for (i
= 0; i
< NUM_CATEGORIES
; ++i
) {
1630 const CategoryDesc
*cdesc
= &predef_categories
[i
];
1631 MonoStringHandle name
;
1633 name
= mono_string_new_utf8_len (domain
, cdesc
->name
, cdesc
->name_length
, error
);
1635 MONO_HANDLE_ARRAY_SETREF (res
, i
, name
);
1637 goto_if_nok (error
, leave
);
1639 for (tmp
= custom_categories
; tmp
; tmp
= tmp
->next
) {
1640 SharedCategory
*scat
= (SharedCategory
*)tmp
->data
;
1641 MonoStringHandle name
;
1643 name
= mono_string_new_utf8_len (domain
, scat
->name
, strlen (scat
->name
), error
);
1645 MONO_HANDLE_ARRAY_SETREF (res
, i
, name
);
1647 goto_if_nok (error
, leave
);
1652 g_slist_free (custom_categories
);
1657 mono_perfcounter_counter_names (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
1659 HANDLE_LOOP_PREPARE
;
1662 SharedCategory
*scat
;
1663 const CategoryDesc
*cdesc
;
1664 MonoArrayHandle res
= NULL_HANDLE_ARRAY
;
1665 MonoDomain
*domain
= mono_domain_get ();
1667 cdesc
= find_category (category
, category_length
);
1669 res
= mono_array_new_handle (domain
, mono_get_string_class (), cdesc
[1].first_counter
- cdesc
->first_counter
, error
);
1670 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1671 for (i
= cdesc
->first_counter
; i
< cdesc
[1].first_counter
; ++i
) {
1672 const CounterDesc
*desc
= &predef_counters
[i
];
1673 MonoStringHandle name
;
1675 name
= mono_string_new_utf8_len (domain
, desc
->name
, desc
->name_length
, error
);
1677 MONO_HANDLE_ARRAY_SETREF (res
, i
- cdesc
->first_counter
, name
);
1679 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1684 scat
= find_custom_category (category
);
1686 SharedCounter
*counter
= custom_category_counters (scat
);
1688 res
= mono_array_new_handle (domain
, mono_get_string_class (), scat
->num_counters
, error
);
1689 if (!is_ok (error
)) {
1690 res
= NULL_HANDLE_ARRAY
;
1694 for (i
= 0; i
< scat
->num_counters
; ++i
) {
1695 MonoStringHandle str
;
1697 str
= mono_string_new_utf8_len (domain
, counter
->name
, strlen (counter
->name
), error
);
1699 MONO_HANDLE_ARRAY_SETREF (res
, i
, str
);
1701 goto_if_nok (error
, leave
);
1702 counter
= next_custom_category_counter (counter
);
1705 res
= mono_array_new_handle (domain
, mono_get_string_class (), 0, error
);
1711 static MonoArrayHandle
1712 get_string_array (void **array
, int count
, gboolean is_process
, MonoError
*error
)
1714 HANDLE_LOOP_PREPARE
;
1717 MonoDomain
*domain
= mono_domain_get ();
1719 MonoArrayHandle res
= mono_array_new_handle (domain
, mono_get_string_class (), count
, error
);
1720 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1721 for (i
= 0; i
< count
; ++i
) {
1725 char *pname
= mono_process_get_name (array
[i
], buf
, sizeof (buf
));
1726 p
= g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array
[i
]), pname
);
1728 sprintf (buf
, "%d", GPOINTER_TO_INT (array
[i
]));
1731 MonoStringHandle str
;
1733 str
= mono_string_new_utf8_len (domain
, p
, strlen (p
), error
);
1737 MONO_HANDLE_ARRAY_SETREF (res
, i
, str
);
1739 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1744 static MonoArrayHandle
1745 get_string_array_of_strings (void **array
, int count
, MonoError
*error
)
1747 HANDLE_LOOP_PREPARE
;
1750 MonoDomain
*domain
= mono_domain_get ();
1752 MonoArrayHandle res
= mono_array_new_handle (domain
, mono_get_string_class (), count
, error
);
1753 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1754 for (i
= 0; i
< count
; ++i
) {
1755 char* p
= (char *)array
[i
];
1756 MonoStringHandle str
;
1758 str
= mono_string_new_utf8_len (domain
, p
, strlen (p
), error
);
1760 MONO_HANDLE_ARRAY_SETREF (res
, i
, str
);
1762 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1768 static MonoArrayHandle
1769 get_mono_instances (MonoError
*error
)
1774 MonoArrayHandle array
;
1779 buf
= g_new (void*, count
);
1780 res
= mono_shared_area_instances (buf
, count
);
1781 } while (res
== count
);
1782 array
= get_string_array (buf
, res
, TRUE
, error
);
1787 static MonoArrayHandle
1788 get_cpu_instances (MonoError
*error
)
1792 MonoArrayHandle array
;
1794 count
= mono_cpu_count () + 1; /* +1 for "_Total" */
1795 buf
= g_new (void*, count
);
1796 for (i
= 0; i
< count
; ++i
)
1797 buf
[i
] = GINT_TO_POINTER (i
- 1); /* -1 => _Total */
1798 array
= get_string_array (buf
, count
, FALSE
, error
);
1800 MonoStringHandle total
= mono_string_new_handle (mono_domain_get (), "_Total", error
);
1801 return_val_if_nok (error
, NULL_HANDLE_ARRAY
);
1802 MONO_HANDLE_ARRAY_SETREF (array
, 0, total
);
1806 static MonoArrayHandle
1807 get_processes_instances (MonoError
*error
)
1809 MonoArrayHandle array
;
1811 void **buf
= mono_process_list (&count
);
1814 return get_string_array (NULL
, 0, FALSE
, error
);
1815 array
= get_string_array (buf
, count
, TRUE
, error
);
1820 static MonoArrayHandle
1821 get_networkinterface_instances (MonoError
*error
)
1823 MonoArrayHandle array
;
1826 void **buf
= mono_networkinterface_list (&count
);
1828 return get_string_array_of_strings (NULL
, 0, error
);
1829 array
= get_string_array_of_strings (buf
, count
, error
);
1830 g_strfreev ((char **) buf
);
1834 static MonoArrayHandle
1835 get_custom_instances (const gunichar2
*category
, MonoError
*error
)
1837 HANDLE_LOOP_PREPARE
;
1839 SharedCategory
*scat
;
1841 scat
= find_custom_category (category
);
1843 GSList
*list
= get_custom_instances_list (scat
);
1846 MonoArrayHandle array
= mono_array_new_handle (mono_domain_get (), mono_get_string_class (), g_slist_length (list
), error
);
1847 if (!is_ok (error
)) {
1848 array
= NULL_HANDLE_ARRAY
;
1851 for (tmp
= list
; tmp
; tmp
= tmp
->next
) {
1852 SharedInstance
*inst
= (SharedInstance
*)tmp
->data
;
1853 MonoStringHandle str
;
1855 str
= mono_string_new_utf8_len (mono_domain_get (), inst
->instance_name
, strlen (inst
->instance_name
), error
);
1857 MONO_HANDLE_ARRAY_SETREF (array
, i
, str
);
1859 if (!is_ok (error
)) {
1860 array
= NULL_HANDLE_ARRAY
;
1866 g_slist_free (list
);
1869 return mono_array_new_handle (mono_domain_get (), mono_get_string_class (), 0, error
);
1873 mono_perfcounter_instance_names (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
1875 const CategoryDesc
* cat
;
1876 MonoArrayHandle result
= NULL_HANDLE_ARRAY
;
1878 cat
= find_category (category
, category_length
);
1880 result
= get_custom_instances (category
, error
);
1883 switch (cat
->instance_type
) {
1885 result
= get_mono_instances (error
);
1888 result
= get_cpu_instances (error
);
1890 case ProcessInstance
:
1891 result
= get_processes_instances (error
);
1893 case NetworkInterfaceInstance
:
1894 result
= get_networkinterface_instances (error
);
1896 case ThreadInstance
: // fallthrough?
1898 result
= mono_array_new_handle (mono_domain_get (), mono_get_string_class (), 0, error
);
1904 PerfCounterEnumCallback cb
;
1906 } PerfCounterForeachData
;
1909 mono_perfcounter_foreach_shared_item (SharedHeader
*header
, gpointer data
)
1913 SharedCategory
*cat
;
1914 SharedCounter
*counter
;
1915 SharedInstance
*inst
;
1916 PerfCounterForeachData
*foreach_data
= (PerfCounterForeachData
*)data
;
1918 if (header
->ftype
== FTYPE_CATEGORY
) {
1919 cat
= (SharedCategory
*)header
;
1920 counter
= custom_category_counters (cat
);
1921 for (i
= 0; i
< cat
->num_counters
; ++i
) {
1922 inst
= custom_get_instance (cat
, counter
, counter
->name
);
1925 addr
= custom_get_value_address (counter
, inst
);
1926 if (!foreach_data
->cb (cat
->name
, counter
->name
, counter
->type
, addr
? *(gint64
*)addr
: 0, foreach_data
->data
))
1928 counter
= next_custom_category_counter (counter
);
1936 mono_perfcounter_foreach (PerfCounterEnumCallback cb
, gpointer data
)
1938 PerfCounterForeachData foreach_data
= { cb
, data
};
1942 foreach_shared_item (mono_perfcounter_foreach_shared_item
, &foreach_data
);
1949 #ifndef ENABLE_NETCORE
1952 mono_perfcounter_get_impl (const gunichar2
*category
, gint32 category_length
,
1953 const gunichar2
*counter
, gint32 counter_length
,
1954 const gunichar2
*instance
, gint32 instance_length
,
1955 gint32
*type
, MonoBoolean
*custom
, MonoError
*error
)
1957 g_assert_not_reached ();
1961 mono_perfcounter_get_sample (void *impl
, MonoBoolean only_value
, MonoCounterSample
*sample
)
1963 g_assert_not_reached ();
1967 mono_perfcounter_update_value (void *impl
, MonoBoolean do_incr
, gint64 value
)
1969 g_assert_not_reached ();
1973 mono_perfcounter_free_data (void *impl
)
1975 g_assert_not_reached ();
1978 /* Category icalls */
1980 mono_perfcounter_category_del (const gunichar2
*name
, gint32 name_length
, MonoError
*error
)
1982 g_assert_not_reached ();
1986 mono_perfcounter_category_help (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
1988 g_assert_not_reached ();
1992 mono_perfcounter_category_exists (const gunichar2
*counter
, gint32 counter_length
, const gunichar2
*category
,
1993 gint32 category_length
, MonoError
*error
)
1995 g_assert_not_reached ();
1999 mono_perfcounter_create (const gunichar2
*category
, gint32 category_length
, const gunichar2
*help
,
2000 gint32 help_length
, gint32 type
, MonoArrayHandle items
, MonoError
*error
)
2002 g_assert_not_reached ();
2006 mono_perfcounter_instance_exists (const gunichar2
*instance
, gint32 instance_length
,
2007 const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
2009 g_assert_not_reached ();
2013 mono_perfcounter_category_names (MonoError
*error
)
2015 g_assert_not_reached ();
2019 mono_perfcounter_counter_names (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
2021 g_assert_not_reached ();
2025 mono_perfcounter_instance_names (const gunichar2
*category
, gint32 category_length
, MonoError
*error
)
2027 g_assert_not_reached ();
2030 #endif /* ENABLE_NETCORE */
2033 mono_perfcounter_foreach (PerfCounterEnumCallback cb
, gpointer data
)
2035 g_assert_not_reached ();