2 * Copyright 2006-2010 Novell
3 * Copyright 2011 Xamarin Inc
4 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 #include "mono-counters.h"
11 #include "mono-proclib.h"
12 #include "mono-os-mutex.h"
26 static MonoCounter *counters = NULL;
27 static mono_mutex_t counters_mutex;
29 static volatile gboolean initialized = FALSE;
31 static int valid_mask = 0;
32 static int set_mask = 0;
34 static GSList *register_callbacks = NULL;
36 static void initialize_system_counters (void);
39 * mono_counter_get_variance:
40 * @counter: counter to get the variance
42 * Variance specifies how the counter value is expected to behave between any two samplings.
44 * Returns: the monotonicity of the counter.
47 mono_counter_get_variance (MonoCounter *counter)
49 return counter->type & MONO_COUNTER_VARIANCE_MASK;
53 * mono_counter_get_unit:
54 * @counter: counter to get the unit
56 * The unit gives a high level view of the unit that the counter is measuring.
58 * Returns: the unit of the counter.
61 mono_counter_get_unit (MonoCounter *counter)
63 return counter->type & MONO_COUNTER_UNIT_MASK;
67 * mono_counter_get_section:
68 * @counter: counter to get the section
70 * Sections are the unit of organization between all counters.
72 * Returns: the section of the counter.
76 mono_counter_get_section (MonoCounter *counter)
78 return counter->type & MONO_COUNTER_SECTION_MASK;
82 * mono_counter_get_type:
83 * @counter: counter to get the type
85 * Returns the type used to strong the value of the counter.
87 * Returns:the type of the counter.
90 mono_counter_get_type (MonoCounter *counter)
92 return counter->type & MONO_COUNTER_TYPE_MASK;
96 * mono_counter_get_name:
97 * @counter: counter to get the name
99 * Returns the counter name. The string should not be freed.
101 * Returns the name of the counter.
105 mono_counter_get_name (MonoCounter *counter)
107 return counter->name;
111 * mono_counter_get_size:
112 * @counter: counter to get the max size of the counter
114 * Use the returned size to create the buffer used with mono_counters_sample
116 * Returns: the max size of the counter data.
119 mono_counter_get_size (MonoCounter *counter)
121 return counter->size;
125 * mono_counters_enable:
126 * @section_mask: a mask listing the sections that will be displayed
128 * This is used to track which counters will be displayed.
131 mono_counters_enable (int section_mask)
133 valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
137 mono_counters_init (void)
142 mono_os_mutex_init (&counters_mutex);
144 initialize_system_counters ();
150 register_internal (const char *name, int type, void *addr, int size)
152 MonoCounter *counter;
153 GSList *register_callback;
155 g_assert (size >= 0);
156 if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
157 type |= MONO_COUNTER_MONOTONIC;
159 mono_os_mutex_lock (&counters_mutex);
161 for (counter = counters; counter; counter = counter->next) {
162 if (counter->addr == addr) {
163 g_warning ("you are registering twice the same counter address");
164 mono_os_mutex_unlock (&counters_mutex);
169 counter = (MonoCounter *) malloc (sizeof (MonoCounter));
171 mono_os_mutex_unlock (&counters_mutex);
174 counter->name = g_strdup (name);
175 counter->type = type;
176 counter->addr = addr;
177 counter->next = NULL;
178 counter->size = size;
184 MonoCounter *item = counters;
187 item->next = counter;
192 for (register_callback = register_callbacks; register_callback; register_callback = register_callback->next)
193 ((MonoCounterRegisterCallback)register_callback->data) (counter);
195 mono_os_mutex_unlock (&counters_mutex);
199 * mono_counters_register:
200 * @name: The name for this counters.
201 * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
202 * @addr: The address to register.
204 * Register addr as the address of a counter of type type.
205 * Note that @name must be a valid string at all times until
206 * mono_counters_dump () is called.
208 * This function should not be used with counter types that require an explicit size such as string
209 * as the counter size will be set to zero making them effectively useless.
212 * It may be a function pointer if MONO_COUNTER_CALLBACK is specified:
213 * the function should return the value and take no arguments.
216 mono_counters_register (const char* name, int type, void *addr)
219 switch (type & MONO_COUNTER_TYPE_MASK) {
220 case MONO_COUNTER_INT:
223 case MONO_COUNTER_UINT:
224 size = sizeof (guint);
226 case MONO_COUNTER_LONG:
227 case MONO_COUNTER_TIME_INTERVAL:
228 size = sizeof (gint64);
230 case MONO_COUNTER_ULONG:
231 size = sizeof (guint64);
233 case MONO_COUNTER_WORD:
234 size = sizeof (gssize);
236 case MONO_COUNTER_DOUBLE:
237 size = sizeof (double);
239 case MONO_COUNTER_STRING:
243 g_assert_not_reached ();
247 g_debug ("counters not enabled");
249 register_internal (name, type, addr, size);
253 * mono_counters_register_with_size:
254 * @name: The name for this counters.
255 * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
256 * @addr: The address to register.
257 * @size: Max size of the counter data.
259 * Register addr as the address of a counter of type @type.
260 * Note that @name must be a valid string at all times until
261 * mono_counters_dump () is called.
263 * It may be a function pointer if MONO_COUNTER_CALLBACK is specified:
264 * the function should return the value and take no arguments.
266 * The value of @size is ignored for types with fixed size such as int and long.
268 * Use @size for types that can have dynamic size such as string.
270 * If @size is negative, it's silently converted to zero.
273 mono_counters_register_with_size (const char *name, int type, void *addr, int size)
276 g_debug ("counters not enabled");
278 register_internal (name, type, addr, size);
282 * mono_counters_on_register
283 * @callback : function to callback when a counter is registered
285 * Add a callback that is going to be called when a counter is registered
288 mono_counters_on_register (MonoCounterRegisterCallback callback)
291 g_debug ("counters not enabled");
295 mono_os_mutex_lock (&counters_mutex);
296 register_callbacks = g_slist_append (register_callbacks, (gpointer) callback);
297 mono_os_mutex_unlock (&counters_mutex);
300 typedef int (*IntFunc) (void);
301 typedef guint (*UIntFunc) (void);
302 typedef gint64 (*LongFunc) (void);
303 typedef guint64 (*ULongFunc) (void);
304 typedef gssize (*PtrFunc) (void);
305 typedef double (*DoubleFunc) (void);
306 typedef char* (*StrFunc) (void);
311 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
317 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
323 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
329 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
335 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
341 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
347 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS);
353 #if defined(TARGET_WIN32)
354 #elif defined(TARGET_MACH)
356 if (getloadavg (load, 3) > 0)
359 char buffer[512], *b;
361 FILE *f = fopen ("/proc/loadavg", "r");
363 len = fread (buffer, 1, sizeof (buffer) - 1, f);
366 buffer [len < 511 ? len : 511] = 0;
368 for (i = 0; i < 3; i++) {
370 return strtod (b, NULL);
397 cpu_load_15min (void)
402 #define SYSCOUNTER_TIME (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
403 #define SYSCOUNTER_BYTES (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
404 #define SYSCOUNTER_COUNT (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_COUNT | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
405 #define SYSCOUNTER_LOAD (MONO_COUNTER_SYSTEM | MONO_COUNTER_DOUBLE | MONO_COUNTER_PERCENTAGE | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
408 initialize_system_counters (void)
410 register_internal ("User Time", SYSCOUNTER_TIME, (gpointer) &user_time, sizeof (gint64));
411 register_internal ("System Time", SYSCOUNTER_TIME, (gpointer) &system_time, sizeof (gint64));
412 register_internal ("Total Time", SYSCOUNTER_TIME, (gpointer) &total_time, sizeof (gint64));
413 register_internal ("Working Set", SYSCOUNTER_BYTES, (gpointer) &working_set, sizeof (gint64));
414 register_internal ("Private Bytes", SYSCOUNTER_BYTES, (gpointer) &private_bytes, sizeof (gint64));
415 register_internal ("Virtual Bytes", SYSCOUNTER_BYTES, (gpointer) &virtual_bytes, sizeof (gint64));
416 register_internal ("Page Faults", SYSCOUNTER_COUNT, (gpointer) &page_faults, sizeof (gint64));
417 register_internal ("CPU Load Average - 1min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_1min, sizeof (double));
418 register_internal ("CPU Load Average - 5min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_5min, sizeof (double));
419 register_internal ("CPU Load Average - 15min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_15min, sizeof (double));
423 * mono_counters_foreach:
424 * @cb: The callback that will be called for each counter.
425 * @user_data: Value passed as second argument of the callback.
427 * Iterate over all counters and call @cb for each one of them. Stop iterating if
428 * the callback returns FALSE.
432 mono_counters_foreach (CountersEnumCallback cb, gpointer user_data)
434 MonoCounter *counter;
437 g_debug ("counters not enabled");
441 mono_os_mutex_lock (&counters_mutex);
443 for (counter = counters; counter; counter = counter->next) {
444 if (!cb (counter, user_data)) {
445 mono_os_mutex_unlock (&counters_mutex);
450 mono_os_mutex_unlock (&counters_mutex);
453 #define COPY_COUNTER(type,functype) do { \
454 size = sizeof (type); \
455 if (buffer_size < size) \
458 *(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
463 sample_internal (MonoCounter *counter, void *buffer, int buffer_size)
465 int cb = counter->type & MONO_COUNTER_CALLBACK;
470 switch (mono_counter_get_type (counter)) {
471 case MONO_COUNTER_INT:
472 COPY_COUNTER (int, IntFunc);
474 case MONO_COUNTER_UINT:
475 COPY_COUNTER (guint, UIntFunc);
477 case MONO_COUNTER_LONG:
478 case MONO_COUNTER_TIME_INTERVAL:
479 COPY_COUNTER (gint64, LongFunc);
481 case MONO_COUNTER_ULONG:
482 COPY_COUNTER (guint64, ULongFunc);
484 case MONO_COUNTER_WORD:
485 COPY_COUNTER (gssize, PtrFunc);
487 case MONO_COUNTER_DOUBLE:
488 COPY_COUNTER (double, DoubleFunc);
490 case MONO_COUNTER_STRING:
491 if (buffer_size < counter->size) {
493 } else if (counter->size == 0) {
496 strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
500 size = counter->size;
501 strncpy ((char *) buffer, strval, size - 1);
502 ((char*)buffer)[size - 1] = '\0';
511 mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
514 g_debug ("counters not enabled");
518 return sample_internal (counter, buffer, buffer_size);
521 #define ENTRY_FMT "%-36s: "
523 dump_counter (MonoCounter *counter, FILE *outfile) {
524 void *buffer = g_malloc0 (counter->size);
525 int size = sample_internal (counter, buffer, counter->size);
527 switch (counter->type & MONO_COUNTER_TYPE_MASK) {
528 case MONO_COUNTER_INT:
529 fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer);
531 case MONO_COUNTER_UINT:
532 fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
534 case MONO_COUNTER_LONG:
535 if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
536 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 10000.0);
538 fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(long long *)buffer);
540 case MONO_COUNTER_ULONG:
541 if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
542 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(guint64*)buffer) / 10000.0);
544 fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(unsigned long long *)buffer);
546 case MONO_COUNTER_WORD:
547 fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
549 case MONO_COUNTER_DOUBLE:
550 fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
552 case MONO_COUNTER_STRING:
553 fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (size == 0) ? "(null)" : (char*)buffer);
555 case MONO_COUNTER_TIME_INTERVAL:
556 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
564 section_names [][12] = {
572 "", // MONO_COUNTER_PERFCOUNTERS - not used.
577 mono_counters_dump_section (int section, int variance, FILE *outfile)
579 MonoCounter *counter = counters;
581 if ((counter->type & section) && (mono_counter_get_variance (counter) & variance))
582 dump_counter (counter, outfile);
583 counter = counter->next;
588 * mono_counters_dump:
589 * @section_mask: The sections to dump counters for
590 * @outfile: a FILE to dump the results to
592 * Displays the counts of all the enabled counters registered.
593 * To filter by variance, you can OR one or more variance with the specific section you want.
594 * Use MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance.
597 mono_counters_dump (int section_mask, FILE *outfile)
601 section_mask &= valid_mask;
606 mono_os_mutex_lock (&counters_mutex);
609 mono_os_mutex_unlock (&counters_mutex);
613 variance = section_mask & MONO_COUNTER_VARIANCE_MASK;
615 /* If no variance mask is supplied, we default to all kinds. */
617 variance = MONO_COUNTER_VARIANCE_MASK;
618 section_mask &= ~MONO_COUNTER_VARIANCE_MASK;
620 for (j = 0, i = MONO_COUNTER_JIT; i < MONO_COUNTER_LAST_SECTION; j++, i <<= 1) {
621 if ((section_mask & i) && (set_mask & i)) {
622 fprintf (outfile, "\n%s statistics\n", section_names [j]);
623 mono_counters_dump_section (i, variance, outfile);
628 mono_os_mutex_unlock (&counters_mutex);
632 * mono_counters_cleanup:
634 * Perform any needed cleanup at process exit.
637 mono_counters_cleanup (void)
639 MonoCounter *counter;
644 mono_os_mutex_lock (&counters_mutex);
649 MonoCounter *tmp = counter;
650 counter = counter->next;
651 free ((void*)tmp->name);
655 mono_os_mutex_unlock (&counters_mutex);
658 static MonoResourceCallback limit_reached = NULL;
659 static uintptr_t resource_limits [MONO_RESOURCE_COUNT * 2];
662 * mono_runtime_resource_check_limit:
663 * @resource_type: one of the #MonoResourceType enum values
664 * @value: the current value of the resource usage
666 * Check if a runtime resource limit has been reached. This function
667 * is intended to be used by the runtime only.
670 mono_runtime_resource_check_limit (int resource_type, uintptr_t value)
674 /* check the hard limit first */
675 if (value > resource_limits [resource_type * 2 + 1]) {
676 limit_reached (resource_type, value, 0);
679 if (value > resource_limits [resource_type * 2])
680 limit_reached (resource_type, value, 1);
684 * mono_runtime_resource_limit:
685 * @resource_type: one of the #MonoResourceType enum values
686 * @soft_limit: the soft limit value
687 * @hard_limit: the hard limit value
689 * This function sets the soft and hard limit for runtime resources. When the limit
690 * is reached, a user-specified callback is called. The callback runs in a restricted
691 * environment, in which the world coult be stopped, so it can't take locks, perform
692 * allocations etc. The callback may be called multiple times once a limit has been reached
693 * if action is not taken to decrease the resource use.
695 * Returns: 0 on error or a positive integer otherwise.
698 mono_runtime_resource_limit (int resource_type, uintptr_t soft_limit, uintptr_t hard_limit)
700 if (resource_type >= MONO_RESOURCE_COUNT || resource_type < 0)
702 if (soft_limit > hard_limit)
704 resource_limits [resource_type * 2] = soft_limit;
705 resource_limits [resource_type * 2 + 1] = hard_limit;
710 * mono_runtime_resource_set_callback:
711 * @callback: a function pointer
713 * Set the callback to be invoked when a resource limit is reached.
714 * The callback will receive the resource type, the resource amount in resource-specific
715 * units and a flag indicating whether the soft or hard limit was reached.
718 mono_runtime_resource_set_callback (MonoResourceCallback callback)
720 limit_reached = callback;