Pass the --clr-memory-model flag on the command line instead of MONO_DEBUG so its...
[mono-project.git] / mono / metadata / mono-perfcounters.c
blob2970e083dc469c143c124b86a53c91044fb198b9
1 /**
2 * \file
4 * Performance counters support.
6 * Author: Paolo Molaro (lupus@ximian.com)
8 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
9 * 2011 Xamarin, Inc
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include "config.h"
14 #include <time.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #if defined (__OpenBSD__)
21 #include <sys/param.h>
22 #endif
23 #ifdef HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #ifdef HAVE_SYS_TIME_H
27 #include <sys/time.h>
28 #endif
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>
34 #endif
35 #if defined (__NetBSD__)
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <sys/vmmeter.h>
39 #endif
40 #include "metadata/mono-perfcounters.h"
41 #include "metadata/appdomain.h"
42 #include "metadata/object-internals.h"
43 /* for mono_stats */
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 {
56 gint64 rawValue;
57 gint64 baseValue;
58 gint64 counterFrequency;
59 gint64 systemFrequency;
60 gint64 timeStamp;
61 gint64 timeStamp100nSec;
62 gint64 counterTimeStamp;
63 int counterType;
66 #ifndef DISABLE_PERFCOUNTERS
67 /* map of PerformanceCounterType.cs */
68 enum {
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,
95 RawBase=0x40030403,
96 CounterMultiBase=0x42030500
99 /* maps a small integer type to the counter types above */
100 static const int
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
118 enum {
119 SingleInstance,
120 MultiInstance,
121 CatTypeUnknown = -1
124 enum {
125 ProcessInstance,
126 ThreadInstance,
127 CPUInstance,
128 MonoInstance,
129 NetworkInterfaceInstance,
130 CustomInstance
133 #define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
134 #define PERFCTR_COUNTER(id,name,help,type,field)
135 enum {
136 #include "mono-perfcounters-def.h"
137 NUM_CATEGORIES
140 #undef PERFCTR_CAT
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 */
145 enum {
146 #include "mono-perfcounters-def.h"
147 END_COUNTERS
150 #undef PERFCTR_CAT
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 */
155 enum {
156 #include "mono-perfcounters-def.h"
157 NUM_COUNTERS
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)
164 typedef struct {
165 char reserved [16];
166 int size;
167 unsigned short counters_start;
168 unsigned short counters_size;
169 unsigned short data_start;
170 MonoPerfCounters counters;
171 char data [1];
172 } MonoSharedArea;
175 binary format of custom counters in shared memory, starting from MonoSharedArea* + data_start;
176 basic stanza:
177 struct stanza_header {
178 byte stanza_type; // FTYPE_*
179 byte other_info;
180 ushort stanza_length; // includeas header
181 ... data ...
184 // strings are utf8
185 // perfcat and perfinstance are 4-bytes aligned
186 struct perfcat {
187 byte typeidx;
188 byte categorytype;
189 ushort length; // includes the counters
190 ushort num_counters;
191 ushort counters_data_size;
192 int num_instances;
193 char name[]; // null terminated
194 char help[]; // null terminated
195 // perfcounters follow
197 byte countertype;
198 char name[]; // null terminated
199 char help[]; // null terminated
201 0-byte
204 struct perfinstance {
205 byte typeidx;
206 byte data_offset; // offset of counters from beginning of struct
207 ushort length;
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
215 enum {
216 FTYPE_CATEGORY = 'C',
217 FTYPE_DELETED = 'D',
218 FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
219 FTYPE_INSTANCE = 'I',
220 FTYPE_DIRTY = 'd',
221 FTYPE_END = 0
224 typedef struct {
225 unsigned char ftype;
226 unsigned char extra;
227 unsigned short size;
228 } SharedHeader;
230 typedef struct {
231 SharedHeader header;
232 unsigned short num_counters;
233 unsigned short counters_data_size;
234 int num_instances;
235 /* variable length data follows */
236 char name [1];
237 // string name
238 // string help
239 // SharedCounter counters_info [num_counters]
240 } SharedCategory;
242 typedef struct {
243 SharedHeader header;
244 unsigned int category_offset;
245 /* variable length data follows */
246 char instance_name [1];
247 // string name
248 } SharedInstance;
250 typedef struct {
251 unsigned char type;
252 guint8 seq_num;
253 /* variable length data follows */
254 char name [1];
255 // string name
256 // string help
257 } SharedCounter;
259 typedef struct {
260 const char *name;
261 const char *help;
262 int name_length;
263 int help_length;
264 unsigned char id;
265 signed int type : 2;
266 unsigned int instance_type : 6;
267 short first_counter;
268 } CategoryDesc;
270 typedef struct {
271 const char *name;
272 const char *help;
273 int name_length;
274 int help_length;
275 short id;
276 unsigned short offset; // offset inside MonoPerfCounters
277 int type;
278 } CounterDesc;
280 #undef PERFCTR_CAT
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}
290 #undef PERFCTR_CAT
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:
302 * *) system counters
303 * *) runtime counters
304 * *) remote 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);
317 struct _ImplVtable {
318 void *arg;
319 SampleFunc sample;
320 UpdateFunc update;
321 CleanupFunc cleanup;
324 typedef struct {
325 int id;
326 char *name;
327 } NetworkVtableArg;
329 typedef struct {
330 ImplVtable vtable;
331 MonoPerfCounters *counters;
332 int pid;
333 } PredefVtable;
335 typedef struct {
336 ImplVtable vtable;
337 SharedInstance *instance_desc;
338 SharedCounter *counter_desc;
339 } CustomVTable;
341 static ImplVtable*
342 create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
344 ImplVtable *vtable = g_new0 (ImplVtable, 1);
345 vtable->arg = arg;
346 vtable->sample = sample;
347 vtable->update = update;
348 return vtable;
351 MonoPerfCounters *mono_perfcounters = NULL;
352 static MonoSharedArea *shared_area = NULL;
354 typedef struct {
355 void *sarea;
356 int refcount;
357 } ExternalSArea;
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)
365 ExternalSArea *data;
366 MonoSharedArea *area = NULL;
368 perfctr_lock ();
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));
372 if (!data) {
373 area = (MonoSharedArea *)mono_shared_area_for_pid (GINT_TO_POINTER (pid));
374 if (area) {
375 data = g_new (ExternalSArea, 1);
376 data->sarea = area;
377 data->refcount = 1;
378 g_hash_table_insert (pid_to_shared_area, GINT_TO_POINTER (pid), data);
380 } else {
381 area = (MonoSharedArea *)data->sarea;
382 data->refcount ++;
384 perfctr_unlock ();
385 return area;
388 static void
389 unref_pid_unlocked (int pid)
391 ExternalSArea *data;
392 data = (ExternalSArea *)g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
393 if (data) {
394 data->refcount--;
395 if (!data->refcount) {
396 g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
397 mono_shared_area_unload (data->sarea);
398 g_free (data);
403 static void
404 predef_cleanup (ImplVtable *vtable)
406 PredefVtable *vt = (PredefVtable*)vtable;
407 /* ExternalSArea *data; */
409 perfctr_lock ();
410 if (!pid_to_shared_area) {
411 perfctr_unlock ();
412 return;
414 unref_pid_unlocked (vt->pid);
415 perfctr_unlock ();
418 static guint64
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__)
428 #ifdef __NetBSD__
429 unsigned long value;
430 #else
431 guint64 value;
432 #endif
433 int mib[2] = {
434 CTL_HW,
435 #ifdef __NetBSD__
436 HW_PHYSMEM64
437 #else
438 HW_MEMSIZE
439 #endif
441 size_t size_sys = sizeof (value);
443 sysctl (mib, 2, &value, &size_sys, NULL, 0);
444 if (value == 0)
445 return 134217728;
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. */
453 #ifdef _SC_PAGESIZE
454 page_size = (guint64)sysconf (_SC_PAGESIZE);
455 #endif
457 #ifdef _SC_PHYS_PAGES
458 num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
459 #endif
461 if (!page_size || !num_pages) {
462 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
463 return 134217728;
466 return page_size * num_pages;
467 #else
468 return 134217728;
469 #endif
472 static guint64
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;
484 guint64 page_size;
485 int mib[2];
486 size_t len;
488 mib[0] = CTL_VM;
489 mib[1] = VM_METER;
491 len = sizeof (vm_total);
492 sysctl (mib, 2, &vm_total, &len, NULL, 0);
494 mib[0] = CTL_HW;
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();
504 vm_size_t page_size;
505 vm_statistics_data_t vmstat;
506 kern_return_t ret;
507 do {
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!");
513 return 0;
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. */
524 #ifdef _SC_PAGESIZE
525 page_size = (guint64)sysconf (_SC_PAGESIZE);
526 #endif
528 #ifdef _SC_AVPHYS_PAGES
529 num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
530 #endif
532 if (!page_size || !num_pages) {
533 g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
534 return 0;
537 return page_size * num_pages;
538 #else
539 return 0;
540 #endif
543 void
544 mono_perfcounters_init (void)
546 int d_offset = G_STRUCT_OFFSET (MonoSharedArea, data);
547 d_offset += 7;
548 d_offset &= ~7;
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;
560 static int
561 perfctr_type_compress (int type)
563 int i;
564 for (i = 0; i < G_N_ELEMENTS (simple_type_to_type); ++i) {
565 if (simple_type_to_type [i] == type)
566 return i;
568 /* NumberOfItems32 */
569 return 2;
572 static SharedHeader*
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;
579 size += 7;
580 size &= ~7;
581 while (p < end) {
582 unsigned short *next;
583 if (*p == FTYPE_END) {
584 if (size < (end - p))
585 goto res;
586 return NULL;
588 if (p + 4 > end)
589 return NULL;
590 next = (unsigned short*)(p + 2);
591 if (*p == FTYPE_DELETED) {
592 /* we reuse only if it's the same size */
593 if (*next == size) {
594 goto res;
597 p += *next;
599 return NULL;
601 res:
602 header = (SharedHeader*)p;
603 header->ftype = ftype;
604 header->extra = 0; /* data_offset could overflow here, so we leave this field unused */
605 header->size = size;
607 return header;
610 typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
612 static void
613 foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
615 while (p < end) {
616 unsigned short *next;
617 if (p + 4 > end)
618 return;
619 next = (unsigned short*)(p + 2);
620 if (!func ((SharedHeader*)p, data))
621 return;
622 if (*p == FTYPE_END)
623 return;
624 p += *next;
628 static void
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);
637 static gboolean
638 mono_utf16_equal_ascii (const gunichar2 *strc, const char *ascii_str)
640 /* FIXME: make this case insensitive */
641 while (*strc == *ascii_str++) {
642 if (*strc == 0)
643 return TRUE;
644 strc++;
646 return *strc == *(const unsigned char *)(ascii_str - 1);
649 static gboolean
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)
654 return FALSE;
655 for (int i = 0; i < utf16_length; ++i) {
656 if (ascii [i] != utf16 [i])
657 return FALSE;
659 return TRUE;
662 typedef struct {
663 const gunichar2 *name;
664 SharedCategory *cat;
665 } CatSearch;
667 static gboolean
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)) {
674 search->cat = cat;
675 return FALSE;
678 return TRUE;
681 static SharedCategory*
682 find_custom_category (const gunichar2 *name)
684 CatSearch search;
685 search.name = name;
686 search.cat = NULL;
687 foreach_shared_item (category_search, &search);
688 return search.cat;
691 static gboolean
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);
698 return TRUE;
701 static GSList*
702 get_custom_categories (void) {
703 GSList *list = NULL;
704 foreach_shared_item (category_collect, &list);
705 return 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)
725 int i;
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))
729 return counter;
730 counter = next_custom_category_counter (counter);
732 return NULL;
735 typedef struct {
736 unsigned int cat_offset;
737 SharedCategory* cat;
738 char *name;
739 SharedInstance* result;
740 GSList *list;
741 } InstanceSearch;
743 static gboolean
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) {
750 if (search->name) {
751 if (strcmp (search->name, ins->instance_name) == 0) {
752 search->result = ins;
753 return FALSE;
755 } else {
756 search->list = g_slist_prepend (search->list, ins);
760 return TRUE;
763 static SharedInstance*
764 find_custom_instance (SharedCategory* cat, char *name)
766 InstanceSearch search;
767 search.cat_offset = (char*)cat - (char*)shared_area;
768 search.cat = cat;
769 search.name = name;
770 search.list = NULL;
771 search.result = NULL;
772 foreach_shared_item (instance_search, &search);
773 return search.result;
776 static GSList*
777 get_custom_instances_list (SharedCategory* cat)
779 InstanceSearch search;
780 search.cat_offset = (char*)cat - (char*)shared_area;
781 search.cat = cat;
782 search.name = NULL;
783 search.list = NULL;
784 search.result = NULL;
785 foreach_shared_item (instance_search, &search);
786 return search.list;
789 static char*
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))
802 return cdesc;
804 return NULL;
807 /* fill the info in sample (except the raw value) */
808 static void
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;
820 static int
821 id_from_string (const gchar *id_str, gboolean is_process)
823 int id = -1;
824 if (strcmp("", id_str) != 0) {
825 char *end;
826 id = strtol (id_str, &end, 0);
827 if (end == id_str && !is_process)
828 id = -1;
830 return id;
833 static MonoBoolean
834 get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
836 MonoProcessError error;
837 int id = GPOINTER_TO_INT (vtable->arg);
838 int pid = id >> 5;
839 id &= 0x1f;
840 if (!only_value) {
841 fill_sample (sample);
842 sample->baseValue = 1;
844 sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
845 switch (id) {
846 case COUNTER_CPU_USER_TIME:
847 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
848 return TRUE;
849 case COUNTER_CPU_PRIV_TIME:
850 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
851 return TRUE;
852 case COUNTER_CPU_INTR_TIME:
853 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
854 return TRUE;
855 case COUNTER_CPU_DCP_TIME:
856 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
857 return TRUE;
858 case COUNTER_CPU_PROC_TIME:
859 sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
860 return TRUE;
862 return FALSE;
865 static void*
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;
870 *custom = FALSE;
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))) {
874 *type = cdesc->type;
875 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_cpu_counter, NULL);
877 return NULL;
880 static MonoBoolean
881 get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
883 MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
884 NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
885 if (!only_value) {
886 fill_sample (sample);
889 sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
890 switch (narg->id) {
891 case COUNTER_NETWORK_BYTESRECSEC:
892 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
893 break;
894 case COUNTER_NETWORK_BYTESSENTSEC:
895 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
896 break;
897 case COUNTER_NETWORK_BYTESTOTALSEC:
898 sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
899 break;
902 if (error == MONO_NETWORK_ERROR_NONE)
903 return TRUE;
904 else
905 return FALSE;
908 static void
909 network_cleanup (ImplVtable *vtable)
911 NetworkVtableArg *narg;
913 if (vtable == NULL)
914 return;
916 narg = (NetworkVtableArg *)vtable->arg;
917 if (narg == NULL)
918 return;
920 g_free (narg->name);
921 narg->name = NULL;
922 g_free (narg);
923 vtable->arg = NULL;
926 static void*
927 network_get_impl (const gunichar2 *counter, int counter_length, const char *instance, int *type, MonoBoolean *custom)
929 const CounterDesc *cdesc;
930 NetworkVtableArg *narg;
931 ImplVtable *vtable;
932 char *instance_name;
934 *custom = FALSE;
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;
940 *type = cdesc->type;
941 vtable = create_vtable (narg, get_network_counter, NULL);
942 vtable->cleanup = network_cleanup;
943 return vtable;
945 return NULL;
948 static MonoBoolean
949 get_process_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
951 int id = GPOINTER_TO_INT (vtable->arg);
952 int pid = id >> 5;
953 if (pid < 0)
954 return FALSE;
955 id &= 0x1f;
956 if (!only_value) {
957 fill_sample (sample);
958 sample->baseValue = 1;
960 sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
961 switch (id) {
962 case COUNTER_PROC_USER_TIME:
963 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
964 return TRUE;
965 case COUNTER_PROC_PRIV_TIME:
966 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
967 return TRUE;
968 case COUNTER_PROC_PROC_TIME:
969 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
970 return TRUE;
971 case COUNTER_PROC_THREADS:
972 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
973 return TRUE;
974 case COUNTER_PROC_VBYTES:
975 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
976 return TRUE;
977 case COUNTER_PROC_WSET:
978 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
979 return TRUE;
980 case COUNTER_PROC_PBYTES:
981 sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
982 return TRUE;
984 return FALSE;
987 static void*
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;
992 *custom = FALSE;
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))) {
996 *type = cdesc->type;
997 return create_vtable (GINT_TO_POINTER (id | cdesc->id), get_process_counter, NULL);
999 return NULL;
1002 static MonoBoolean
1003 mono_mem_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1005 int id = GPOINTER_TO_INT (vtable->arg);
1006 if (!only_value) {
1007 fill_sample (sample);
1008 sample->baseValue = 1;
1010 sample->counterType = predef_counters [predef_categories [CATEGORY_MONO_MEM].first_counter + id].type;
1011 switch (id) {
1012 case COUNTER_MEM_NUM_OBJECTS:
1013 sample->rawValue = 0;
1014 return TRUE;
1015 case COUNTER_MEM_PHYS_TOTAL:
1016 sample->rawValue = mono_determine_physical_ram_size ();
1017 return TRUE;
1018 case COUNTER_MEM_PHYS_AVAILABLE:
1019 sample->rawValue = mono_determine_physical_ram_available_size ();
1020 return TRUE;
1022 return FALSE;
1025 static void*
1026 mono_mem_get_impl (const gunichar2 *counter, int counter_length, const char *instance, int *type, MonoBoolean *custom)
1028 const CounterDesc *cdesc;
1029 *custom = FALSE;
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);
1034 return NULL;
1037 static MonoBoolean
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;
1044 cat_id &= 0xffff;
1045 if (!only_value) {
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);
1054 return TRUE;
1057 static ImplVtable*
1058 predef_vtable (void *arg, const gchar *pids)
1060 MonoSharedArea *area;
1061 PredefVtable *vtable;
1062 int pid;
1064 pid = atoi (pids);
1065 area = load_sarea_for_pid (pid);
1066 if (!area)
1067 return NULL;
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);
1074 vtable->pid = pid;
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.
1082 static MonoBoolean
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;
1087 cat_id &= 0xffff;
1088 if (!only_value) {
1089 fill_sample (sample);
1090 sample->baseValue = 1;
1092 sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
1093 switch (cat_id) {
1094 case CATEGORY_EXC:
1095 switch (id) {
1096 case COUNTER_EXC_THROWN:
1097 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->exceptions_thrown);
1098 return TRUE;
1100 break;
1101 case CATEGORY_ASPNET:
1102 switch (id) {
1103 case COUNTER_ASPNET_REQ_Q:
1104 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->aspnet_requests_queued);
1105 return TRUE;
1106 case COUNTER_ASPNET_REQ_TOTAL:
1107 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->aspnet_requests);
1108 return TRUE;
1110 break;
1111 case CATEGORY_THREADPOOL:
1112 switch (id) {
1113 case COUNTER_THREADPOOL_WORKITEMS:
1114 sample->rawValue = mono_atomic_load_i64 (&mono_perfcounters->threadpool_workitems);
1115 return TRUE;
1116 case COUNTER_THREADPOOL_IOWORKITEMS:
1117 sample->rawValue = mono_atomic_load_i64 (&mono_perfcounters->threadpool_ioworkitems);
1118 return TRUE;
1119 case COUNTER_THREADPOOL_THREADS:
1120 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->threadpool_threads);
1121 return TRUE;
1122 case COUNTER_THREADPOOL_IOTHREADS:
1123 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->threadpool_iothreads);
1124 return TRUE;
1126 break;
1127 case CATEGORY_JIT:
1128 switch (id) {
1129 case COUNTER_JIT_BYTES:
1130 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_bytes);
1131 return TRUE;
1132 case COUNTER_JIT_METHODS:
1133 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_methods);
1134 return TRUE;
1135 case COUNTER_JIT_TIME:
1136 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_time);
1137 return TRUE;
1138 case COUNTER_JIT_BYTES_PSEC:
1139 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_bytes);
1140 return TRUE;
1141 case COUNTER_JIT_FAILURES:
1142 sample->rawValue = mono_atomic_load_i32 (&mono_perfcounters->jit_failures);
1143 return TRUE;
1145 break;
1147 return FALSE;
1150 static gint64
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;
1157 cat_id &= 0xffff;
1158 switch (cat_id) {
1159 case CATEGORY_ASPNET:
1160 switch (id) {
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;
1164 break;
1165 case CATEGORY_THREADPOOL:
1166 switch (id) {
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;
1172 break;
1174 if (ptr) {
1175 if (do_incr) {
1176 if (value == 1)
1177 return mono_atomic_inc_i32 (ptr);
1178 if (value == -1)
1179 return mono_atomic_dec_i32 (ptr);
1181 return mono_atomic_add_i32 (ptr, (gint32)value);
1183 /* this can be non-atomic */
1184 *ptr = value;
1185 return value;
1186 } else if (ptr64) {
1187 if (do_incr) {
1188 if (value == 1)
1189 return UnlockedIncrement64 (ptr64); /* FIXME: use mono_atomic_inc_i64 () */
1190 if (value == -1)
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 */
1196 *ptr64 = value;
1197 return value;
1199 return 0;
1202 static void*
1203 predef_writable_get_impl (int cat, const gunichar2 *counter, int counter_length, const char *instance, int *type, MonoBoolean *custom)
1205 const CounterDesc *cdesc;
1206 *custom = TRUE;
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);
1211 else
1212 return predef_vtable (GINT_TO_POINTER ((cdesc->id << 16) | cat), instance);
1214 return NULL;
1217 static MonoBoolean
1218 custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
1220 CustomVTable *counter_data = (CustomVTable *)vtable;
1221 if (!only_value) {
1222 fill_sample (sample);
1223 sample->baseValue = 1;
1225 sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
1226 if (!vtable->arg)
1227 sample->rawValue = 0;
1228 else
1229 sample->rawValue = *(guint64*)vtable->arg;
1230 return TRUE;
1233 static gint64
1234 custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
1236 /* FIXME: check writability */
1237 guint64 *ptr = (guint64 *)vtable->arg;
1238 if (ptr) {
1239 if (do_incr) {
1240 /* FIXME: we need to do this atomically */
1241 *ptr += value;
1242 return *ptr;
1244 /* this can be non-atomic */
1245 *ptr = value;
1246 return value;
1248 return 0;
1251 static SharedInstance*
1252 custom_get_instance (SharedCategory *cat, SharedCounter *scounter, char* name)
1254 SharedInstance* inst;
1255 char *p;
1256 int size;
1257 inst = find_custom_instance (cat, name);
1258 if (inst)
1259 return inst;
1260 size = sizeof (SharedInstance) + strlen (name);
1261 size += 7;
1262 size &= ~7;
1263 size += (sizeof (guint64) * cat->num_counters);
1264 perfctr_lock ();
1265 inst = (SharedInstance*) shared_data_reserve_room (size, FTYPE_INSTANCE);
1266 if (!inst) {
1267 perfctr_unlock ();
1268 return NULL;
1270 inst->category_offset = (char*)cat - (char*)shared_area;
1271 cat->num_instances++;
1272 /* now copy the variable data */
1273 p = inst->instance_name;
1274 strcpy (p, name);
1275 p += strlen (name) + 1;
1276 perfctr_unlock ();
1278 return inst;
1281 static ImplVtable*
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;
1295 static gpointer
1296 custom_get_value_address (SharedCounter *scounter, SharedInstance* sinst)
1298 int offset = sizeof (SharedInstance) + strlen (sinst->instance_name);
1299 offset += 7;
1300 offset &= ~7;
1301 offset += scounter->seq_num * sizeof (guint64);
1302 return (char*)sinst + offset;
1305 static void*
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;
1310 char *name;
1312 error_init (error);
1313 scounter = find_custom_counter (cat, counter);
1314 if (!scounter)
1315 return NULL;
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);
1320 g_free (name);
1321 if (!inst)
1322 return NULL;
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)
1329 int i;
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];
1334 return NULL;
1337 void*
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);
1346 if (!cdesc) {
1347 SharedCategory *scat = find_custom_category (category);
1348 if (!scat)
1349 return NULL;
1350 *custom = TRUE;
1351 result = custom_get_impl (scat, counter, counter_length, instance, type, error);
1352 return_val_if_nok (error, NULL);
1353 return result;
1355 char *c_instance = mono_utf16_to_utf8 (instance, instance_length, error);
1356 return_val_if_nok (error, NULL);
1357 switch (cdesc->id) {
1358 case CATEGORY_CPU:
1359 result = cpu_get_impl (counter, counter_length, c_instance, type, custom);
1360 break;
1361 case CATEGORY_PROC:
1362 result = process_get_impl (counter, counter_length, c_instance, type, custom);
1363 break;
1364 case CATEGORY_MONO_MEM:
1365 result = mono_mem_get_impl (counter, counter_length, c_instance, type, custom);
1366 break;
1367 case CATEGORY_NETWORK:
1368 result = network_get_impl (counter, counter_length, c_instance, type, custom);
1369 break;
1370 case CATEGORY_JIT:
1371 case CATEGORY_EXC:
1372 case CATEGORY_GC:
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);
1381 break;
1383 g_free (c_instance);
1384 return result;
1387 MonoBoolean
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);
1393 return FALSE;
1396 gint64
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);
1402 return 0;
1405 void
1406 mono_perfcounter_free_data (void *impl)
1408 ImplVtable *vtable = (ImplVtable *)impl;
1409 if (vtable && vtable->cleanup)
1410 vtable->cleanup (vtable);
1411 g_free (impl);
1414 /* Category icalls */
1415 MonoBoolean
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 */
1422 if (cdesc)
1423 return FALSE;
1424 perfctr_lock ();
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) {
1428 perfctr_unlock ();
1429 return FALSE;
1431 cat->header.ftype = FTYPE_DELETED;
1432 perfctr_unlock ();
1433 return TRUE;
1436 /* this is an icall */
1437 MonoStringHandle
1438 mono_perfcounter_category_help (const gunichar2 *category, gint32 category_length, MonoError *error)
1440 MonoStringHandle result = NULL_HANDLE_STRING;
1441 const CategoryDesc *cdesc;
1442 error_init (error);
1443 cdesc = find_category (category, category_length);
1444 if (!cdesc) {
1445 SharedCategory *scat = find_custom_category (category);
1446 if (!scat)
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);
1450 return result;
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);
1454 return result;
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.
1461 MonoBoolean
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);
1467 if (!cdesc) {
1468 SharedCategory *scat = find_custom_category (category);
1469 if (!scat)
1470 return FALSE;
1471 /* counter is allowed to be null */
1472 if (!counter)
1473 return TRUE;
1474 /* search through the custom category */
1475 return find_custom_counter (scat, counter) != NULL;
1477 /* counter is allowed to be null */
1478 if (!counter)
1479 return TRUE;
1480 if (get_counter_in_category (cdesc, counter, counter_length))
1481 return TRUE;
1482 return FALSE;
1485 /* C map of the type with the same name */
1486 typedef struct {
1487 MonoObject object;
1488 MonoString *help;
1489 MonoString *name;
1490 int type;
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.
1499 MonoBoolean
1500 mono_perfcounter_create (const gunichar2 *category, gint32 category_length, const gunichar2 *help,
1501 gint32 help_length, gint32 type, MonoArrayHandle items, MonoError *error)
1503 int result = FALSE;
1504 int i, j = 0, k, size;
1505 int num_counters = mono_array_handle_length (items);
1506 int counters_data_size;
1507 char *name = NULL;
1508 char *chelp = NULL;
1509 char **counter_info = NULL;
1510 char *p;
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) {
1528 if (k)
1529 MONO_HANDLE_GET (str, data, help);
1530 else
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])
1535 goto failure;
1536 size += utf8_length + 1;
1537 ++j;
1539 size += sizeof (SharedCounter) + 1; /* FIXME +1 should be -1 */
1541 size += 7;
1542 size &= ~7;
1543 counters_data_size = num_counters * 8; /* optimize for size later */
1544 if (size > 65535)
1545 goto failure;
1546 perfctr_lock ();
1547 cat = (SharedCategory*) shared_data_reserve_room (size, FTYPE_CATEGORY);
1548 if (!cat) {
1549 perfctr_unlock ();
1550 goto failure;
1552 cat->num_counters = num_counters;
1553 cat->counters_data_size = counters_data_size;
1554 /* now copy the vaiable data */
1555 p = cat->name;
1556 strcpy (p, name);
1557 p += strlen (name) + 1;
1558 strcpy (p, chelp);
1559 p += strlen (chelp) + 1;
1560 j = 0;
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));
1565 *p++ = i;
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;
1572 perfctr_unlock ();
1573 result = TRUE;
1574 failure:
1575 if (counter_info) {
1576 for (i = 0; i < num_counters * 2; ++i) {
1577 g_free (counter_info [i]);
1579 g_free (counter_info);
1581 g_free (name);
1582 g_free (chelp);
1583 return result;
1586 MonoBoolean
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;
1592 char *name;
1593 cdesc = find_category (category, category_length);
1594 if (!cdesc) {
1595 SharedCategory *scat;
1596 scat = find_custom_category (category);
1597 if (!scat)
1598 return FALSE;
1599 name = mono_utf16_to_utf8 (instance, instance_length, error);
1600 return_val_if_nok (error, FALSE);
1601 sinst = find_custom_instance (scat, name);
1602 g_free (name);
1603 if (sinst)
1604 return TRUE;
1605 } else {
1606 /* FIXME: search instance */
1608 return FALSE;
1611 /* this is an icall */
1612 MonoArrayHandle
1613 mono_perfcounter_category_names (MonoError *error)
1615 HANDLE_LOOP_PREPARE;
1617 int i;
1618 MonoArrayHandle res;
1619 MonoDomain *domain = mono_domain_get ();
1620 GSList *custom_categories, *tmp;
1621 perfctr_lock ();
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;
1626 goto leave;
1629 for (i = 0; i < NUM_CATEGORIES; ++i) {
1630 const CategoryDesc *cdesc = &predef_categories [i];
1631 MonoStringHandle name;
1632 SETUP_ICALL_FRAME;
1633 name = mono_string_new_utf8_len (domain, cdesc->name, cdesc->name_length, error);
1634 if (is_ok (error))
1635 MONO_HANDLE_ARRAY_SETREF (res, i, name);
1636 CLEAR_ICALL_FRAME;
1637 goto_if_nok (error, leave);
1639 for (tmp = custom_categories; tmp; tmp = tmp->next) {
1640 SharedCategory *scat = (SharedCategory *)tmp->data;
1641 MonoStringHandle name;
1642 SETUP_ICALL_FRAME;
1643 name = mono_string_new_utf8_len (domain, scat->name, strlen (scat->name), error);
1644 if (is_ok (error))
1645 MONO_HANDLE_ARRAY_SETREF (res, i, name);
1646 CLEAR_ICALL_FRAME;
1647 goto_if_nok (error, leave);
1648 i++;
1650 leave:
1651 perfctr_unlock ();
1652 g_slist_free (custom_categories);
1653 return res;
1656 MonoArrayHandle
1657 mono_perfcounter_counter_names (const gunichar2 *category, gint32 category_length, MonoError *error)
1659 HANDLE_LOOP_PREPARE;
1661 int i;
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);
1668 if (cdesc) {
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;
1674 SETUP_ICALL_FRAME;
1675 name = mono_string_new_utf8_len (domain, desc->name, desc->name_length, error);
1676 if (is_ok (error))
1677 MONO_HANDLE_ARRAY_SETREF (res, i - cdesc->first_counter, name);
1678 CLEAR_ICALL_FRAME;
1679 return_val_if_nok (error, NULL_HANDLE_ARRAY);
1681 return res;
1683 perfctr_lock ();
1684 scat = find_custom_category (category);
1685 if (scat) {
1686 SharedCounter *counter = custom_category_counters (scat);
1687 int i;
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;
1691 goto leave;
1694 for (i = 0; i < scat->num_counters; ++i) {
1695 MonoStringHandle str;
1696 SETUP_ICALL_FRAME;
1697 str = mono_string_new_utf8_len (domain, counter->name, strlen (counter->name), error);
1698 if (is_ok (error))
1699 MONO_HANDLE_ARRAY_SETREF (res, i, str);
1700 CLEAR_ICALL_FRAME;
1701 goto_if_nok (error, leave);
1702 counter = next_custom_category_counter (counter);
1704 } else
1705 res = mono_array_new_handle (domain, mono_get_string_class (), 0, error);
1706 leave:
1707 perfctr_unlock ();
1708 return res;
1711 static MonoArrayHandle
1712 get_string_array (void **array, int count, gboolean is_process, MonoError *error)
1714 HANDLE_LOOP_PREPARE;
1716 int i;
1717 MonoDomain *domain = mono_domain_get ();
1718 error_init (error);
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) {
1722 char buf [128];
1723 char *p;
1724 if (is_process) {
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);
1727 } else {
1728 sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
1729 p = buf;
1731 MonoStringHandle str;
1732 SETUP_ICALL_FRAME;
1733 str = mono_string_new_utf8_len (domain, p, strlen (p), error);
1734 if (p != buf)
1735 g_free (p);
1736 if (is_ok (error))
1737 MONO_HANDLE_ARRAY_SETREF (res, i, str);
1738 CLEAR_ICALL_FRAME;
1739 return_val_if_nok (error, NULL_HANDLE_ARRAY);
1741 return res;
1744 static MonoArrayHandle
1745 get_string_array_of_strings (void **array, int count, MonoError *error)
1747 HANDLE_LOOP_PREPARE;
1749 int i;
1750 MonoDomain *domain = mono_domain_get ();
1751 error_init (error);
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;
1757 SETUP_ICALL_FRAME;
1758 str = mono_string_new_utf8_len (domain, p, strlen (p), error);
1759 if (is_ok (error))
1760 MONO_HANDLE_ARRAY_SETREF (res, i, str);
1761 CLEAR_ICALL_FRAME;
1762 return_val_if_nok (error, NULL_HANDLE_ARRAY);
1765 return res;
1768 static MonoArrayHandle
1769 get_mono_instances (MonoError *error)
1771 int count = 64;
1772 int res;
1773 void **buf = NULL;
1774 MonoArrayHandle array;
1775 error_init (error);
1776 do {
1777 count *= 2;
1778 g_free (buf);
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);
1783 g_free (buf);
1784 return array;
1787 static MonoArrayHandle
1788 get_cpu_instances (MonoError *error)
1790 void **buf = NULL;
1791 int i, count;
1792 MonoArrayHandle array;
1793 error_init (error);
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);
1799 g_free (buf);
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);
1803 return array;
1806 static MonoArrayHandle
1807 get_processes_instances (MonoError *error)
1809 MonoArrayHandle array;
1810 int count = 0;
1811 void **buf = mono_process_list (&count);
1812 error_init (error);
1813 if (!buf)
1814 return get_string_array (NULL, 0, FALSE, error);
1815 array = get_string_array (buf, count, TRUE, error);
1816 g_free (buf);
1817 return array;
1820 static MonoArrayHandle
1821 get_networkinterface_instances (MonoError *error)
1823 MonoArrayHandle array;
1824 int count = 0;
1825 error_init (error);
1826 void **buf = mono_networkinterface_list (&count);
1827 if (!buf)
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);
1831 return array;
1834 static MonoArrayHandle
1835 get_custom_instances (const gunichar2 *category, MonoError *error)
1837 HANDLE_LOOP_PREPARE;
1839 SharedCategory *scat;
1840 error_init (error);
1841 scat = find_custom_category (category);
1842 if (scat) {
1843 GSList *list = get_custom_instances_list (scat);
1844 GSList *tmp;
1845 int i = 0;
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;
1849 goto exit;
1851 for (tmp = list; tmp; tmp = tmp->next) {
1852 SharedInstance *inst = (SharedInstance *)tmp->data;
1853 MonoStringHandle str;
1854 SETUP_ICALL_FRAME;
1855 str = mono_string_new_utf8_len (mono_domain_get (), inst->instance_name, strlen (inst->instance_name), error);
1856 if (is_ok (error))
1857 MONO_HANDLE_ARRAY_SETREF (array, i, str);
1858 CLEAR_ICALL_FRAME;
1859 if (!is_ok (error)) {
1860 array = NULL_HANDLE_ARRAY;
1861 goto exit;
1863 i++;
1865 exit:
1866 g_slist_free (list);
1867 return array;
1869 return mono_array_new_handle (mono_domain_get (), mono_get_string_class (), 0, error);
1872 MonoArrayHandle
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);
1879 if (!cat) {
1880 result = get_custom_instances (category, error);
1881 return result;
1883 switch (cat->instance_type) {
1884 case MonoInstance:
1885 result = get_mono_instances (error);
1886 break;
1887 case CPUInstance:
1888 result = get_cpu_instances (error);
1889 break;
1890 case ProcessInstance:
1891 result = get_processes_instances (error);
1892 break;
1893 case NetworkInterfaceInstance:
1894 result = get_networkinterface_instances (error);
1895 break;
1896 case ThreadInstance: // fallthrough?
1897 default:
1898 result = mono_array_new_handle (mono_domain_get (), mono_get_string_class (), 0, error);
1900 return result;
1903 typedef struct {
1904 PerfCounterEnumCallback cb;
1905 void *data;
1906 } PerfCounterForeachData;
1908 static gboolean
1909 mono_perfcounter_foreach_shared_item (SharedHeader *header, gpointer data)
1911 int i;
1912 void *addr;
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);
1923 if (!inst)
1924 return FALSE;
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))
1927 return FALSE;
1928 counter = next_custom_category_counter (counter);
1932 return TRUE;
1935 void
1936 mono_perfcounter_foreach (PerfCounterEnumCallback cb, gpointer data)
1938 PerfCounterForeachData foreach_data = { cb, data };
1940 perfctr_lock ();
1942 foreach_shared_item (mono_perfcounter_foreach_shared_item, &foreach_data);
1944 perfctr_unlock ();
1947 #else
1949 void*
1950 mono_perfcounter_get_impl (const gunichar2 *category, gint32 category_length,
1951 const gunichar2 *counter, gint32 counter_length,
1952 const gunichar2 *instance, gint32 instance_length,
1953 gint32 *type, MonoBoolean *custom, MonoError *error)
1955 g_assert_not_reached ();
1958 MonoBoolean
1959 mono_perfcounter_get_sample (void *impl, MonoBoolean only_value, MonoCounterSample *sample)
1961 g_assert_not_reached ();
1964 gint64
1965 mono_perfcounter_update_value (void *impl, MonoBoolean do_incr, gint64 value)
1967 g_assert_not_reached ();
1970 void
1971 mono_perfcounter_free_data (void *impl)
1973 g_assert_not_reached ();
1976 /* Category icalls */
1977 MonoBoolean
1978 mono_perfcounter_category_del (const gunichar2 *name, gint32 name_length, MonoError *error)
1980 g_assert_not_reached ();
1983 MonoStringHandle
1984 mono_perfcounter_category_help (const gunichar2 *category, gint32 category_length, MonoError *error)
1986 g_assert_not_reached ();
1989 MonoBoolean
1990 mono_perfcounter_category_exists (const gunichar2 *counter, gint32 counter_length, const gunichar2 *category,
1991 gint32 category_length, MonoError *error)
1993 g_assert_not_reached ();
1996 MonoBoolean
1997 mono_perfcounter_create (const gunichar2 *category, gint32 category_length, const gunichar2 *help,
1998 gint32 help_length, gint32 type, MonoArrayHandle items, MonoError *error)
2000 g_assert_not_reached ();
2003 MonoBoolean
2004 mono_perfcounter_instance_exists (const gunichar2 *instance, gint32 instance_length,
2005 const gunichar2 *category, gint32 category_length, MonoError *error)
2007 g_assert_not_reached ();
2010 MonoArrayHandle
2011 mono_perfcounter_category_names (MonoError *error)
2013 g_assert_not_reached ();
2016 MonoArrayHandle
2017 mono_perfcounter_counter_names (const gunichar2 *category, gint32 category_length, MonoError *error)
2019 g_assert_not_reached ();
2022 MonoArrayHandle
2023 mono_perfcounter_instance_names (const gunichar2 *category, gint32 category_length, MonoError *error)
2025 g_assert_not_reached ();
2028 #endif