Add logic to object array typecheck to handle arrays of unmanaged pointers (#14733)
[mono-project.git] / mono / utils / mono-counters.c
blob012aa16a4562a21e6561ccafc79e23effc09ac5a
1 /**
2 * \file
3 * Copyright 2006-2010 Novell
4 * Copyright 2011 Xamarin Inc
5 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
6 */
8 #include <stdlib.h>
9 #include <glib.h>
10 #include "config.h"
11 #include "mono-counters.h"
12 #include "mono-proclib.h"
13 #include "mono-os-mutex.h"
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
19 struct _MonoCounter {
20 MonoCounter *next;
21 const char *name;
22 void *addr;
23 int type;
24 size_t size;
27 static MonoCounter *counters = NULL;
28 static mono_mutex_t counters_mutex;
30 static volatile gboolean initialized = FALSE;
32 static int valid_mask = 0;
33 static int set_mask = 0;
35 static GSList *register_callbacks = NULL;
37 static void initialize_system_counters (void);
39 /**
40 * mono_counter_get_variance:
41 * \param counter counter to get the variance
43 * Variance specifies how the counter value is expected to behave between any two samplings.
45 * \returns the monotonicity of the counter.
47 int
48 mono_counter_get_variance (MonoCounter *counter)
50 return counter->type & MONO_COUNTER_VARIANCE_MASK;
53 /**
54 * mono_counter_get_unit:
55 * \param counter counter to get the unit
57 * The unit gives a high level view of the unit that the counter is measuring.
59 * \returns the unit of the counter.
61 int
62 mono_counter_get_unit (MonoCounter *counter)
64 return counter->type & MONO_COUNTER_UNIT_MASK;
67 /**
68 * mono_counter_get_section:
69 * \param counter counter to get the section
70 * Sections are the unit of organization between all counters.
71 * \returns the section of the counter.
74 int
75 mono_counter_get_section (MonoCounter *counter)
77 return counter->type & MONO_COUNTER_SECTION_MASK;
80 /**
81 * mono_counter_get_type:
82 * \param counter counter to get the type
83 * \returns the type used to store the value of the counter.
85 int
86 mono_counter_get_type (MonoCounter *counter)
88 return counter->type & MONO_COUNTER_TYPE_MASK;
91 /**
92 * mono_counter_get_name:
93 * \param counter counter to get the name
94 * \returns the counter name. The string should not be freed.
97 const char*
98 mono_counter_get_name (MonoCounter *counter)
100 return counter->name;
104 * mono_counter_get_size:
105 * \param counter counter to get the max size of the counter
106 * Use the returned size to create the buffer used with \c mono_counters_sample
107 * \returns the max size of the counter data.
109 size_t
110 mono_counter_get_size (MonoCounter *counter)
112 return counter->size;
116 * mono_counters_enable:
117 * \param sectionmask a mask listing the sections that will be displayed
118 * This is used to track which counters will be displayed.
120 void
121 mono_counters_enable (int section_mask)
123 valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
126 void
127 mono_counters_init (void)
129 if (initialized)
130 return;
132 mono_os_mutex_init (&counters_mutex);
134 initialize_system_counters ();
136 initialized = TRUE;
139 static void
140 register_internal (const char *name, int type, void *addr, int size)
142 MonoCounter *counter;
143 GSList *register_callback;
145 g_assert (size >= 0);
146 if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
147 type |= MONO_COUNTER_MONOTONIC;
149 mono_os_mutex_lock (&counters_mutex);
151 for (counter = counters; counter; counter = counter->next) {
152 if (counter->addr == addr) {
153 g_warning ("you are registering the same counter address twice: %s at %p", name, addr);
154 mono_os_mutex_unlock (&counters_mutex);
155 return;
159 counter = (MonoCounter *) g_malloc (sizeof (MonoCounter));
160 if (!counter) {
161 mono_os_mutex_unlock (&counters_mutex);
162 return;
164 counter->name = g_strdup (name);
165 counter->type = type;
166 counter->addr = addr;
167 counter->next = NULL;
168 counter->size = size;
170 set_mask |= type;
172 /* Append */
173 if (counters) {
174 MonoCounter *item = counters;
175 while (item->next)
176 item = item->next;
177 item->next = counter;
178 } else {
179 counters = counter;
182 for (register_callback = register_callbacks; register_callback; register_callback = register_callback->next)
183 ((MonoCounterRegisterCallback)register_callback->data) (counter);
185 mono_os_mutex_unlock (&counters_mutex);
189 * mono_counters_register:
190 * \param name The name for this counters.
191 * \param type One of the possible \c MONO_COUNTER types, or \c MONO_COUNTER_CALLBACK for a function pointer.
192 * \param addr The address to register.
194 * Register \p addr as the address of a counter of type type.
195 * Note that \p name must be a valid string at all times until
196 * \c mono_counters_dump() is called.
198 * This function should not be used with counter types that require an explicit size such as string
199 * as the counter size will be set to zero making them effectively useless.
201 * It may be a function pointer if \c MONO_COUNTER_CALLBACK is specified:
202 * the function should return the value and take no arguments.
204 void
205 mono_counters_register (const char* name, int type, void *addr)
207 int size;
208 switch (type & MONO_COUNTER_TYPE_MASK) {
209 case MONO_COUNTER_INT:
210 size = sizeof (int);
211 break;
212 case MONO_COUNTER_UINT:
213 size = sizeof (guint);
214 break;
215 case MONO_COUNTER_LONG:
216 case MONO_COUNTER_TIME_INTERVAL:
217 size = sizeof (gint64);
218 break;
219 case MONO_COUNTER_ULONG:
220 size = sizeof (guint64);
221 break;
222 case MONO_COUNTER_WORD:
223 size = sizeof (gssize);
224 break;
225 case MONO_COUNTER_DOUBLE:
226 size = sizeof (double);
227 break;
228 case MONO_COUNTER_STRING:
229 size = 0;
230 break;
231 default:
232 g_assert_not_reached ();
235 if (!initialized)
236 g_debug ("counters not enabled");
237 else
238 register_internal (name, type, addr, size);
242 * mono_counters_register_with_size:
243 * \param name The name for this counters.
244 * \param type One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
245 * \param addr The address to register.
246 * \param size Max size of the counter data.
248 * Register \p addr as the address of a counter of type \p type.
249 * Note that \p name must be a valid string at all times until
250 * \c mono_counters_dump() is called.
252 * It may be a function pointer if \c MONO_COUNTER_CALLBACK is specified:
253 * the function should return the value and take no arguments.
255 * The value of \p size is ignored for types with fixed size such as int and long.
257 * Use \p size for types that can have dynamic size such as string.
259 * If \p size is negative, it's silently converted to zero.
261 void
262 mono_counters_register_with_size (const char *name, int type, void *addr, int size)
264 if (!initialized)
265 g_debug ("counters not enabled");
266 else
267 register_internal (name, type, addr, size);
271 * mono_counters_on_register
272 * \param callback function to callback when a counter is registered
273 * Add a callback that is going to be called when a counter is registered
275 void
276 mono_counters_on_register (MonoCounterRegisterCallback callback)
278 if (!initialized) {
279 g_debug ("counters not enabled");
280 return;
283 mono_os_mutex_lock (&counters_mutex);
284 register_callbacks = g_slist_append (register_callbacks, (gpointer) callback);
285 mono_os_mutex_unlock (&counters_mutex);
288 typedef int (*IntFunc) (void);
289 typedef guint (*UIntFunc) (void);
290 typedef gint64 (*LongFunc) (void);
291 typedef guint64 (*ULongFunc) (void);
292 typedef gssize (*PtrFunc) (void);
293 typedef double (*DoubleFunc) (void);
294 typedef char* (*StrFunc) (void);
296 static gint64
297 user_time (void)
299 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
302 static gint64
303 system_time (void)
305 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
308 static gint64
309 total_time (void)
311 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
314 static gint64
315 working_set (void)
317 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
320 static gint64
321 private_bytes (void)
323 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
326 static gint64
327 virtual_bytes (void)
329 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
332 static gint64
333 page_faults (void)
335 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS);
338 static gint64
339 paged_bytes (void)
341 return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PAGED_BYTES);
345 // If cpu_load gets inlined on Windows then cpu_load_1min, cpu_load_5min and cpu_load_15min can be folded into a single function and that will
346 // cause a failure when registering counters since the same function address will be used by all three functions. Preventing this method from being inlined
347 // will make sure the registered callback functions remains unique.
348 #ifdef _MSC_VER
349 __declspec(noinline)
350 #endif
351 static double
352 cpu_load (int kind)
354 #if defined(TARGET_WIN32)
355 #elif defined(TARGET_MACH)
356 double load [3];
357 if (getloadavg (load, 3) > 0)
358 return load [kind];
359 #else
360 char buffer[512], *b;
361 int len, i;
362 FILE *f = fopen ("/proc/loadavg", "r");
363 if (f) {
364 len = fread (buffer, 1, sizeof (buffer) - 1, f);
365 fclose (f);
366 if (len > 0) {
367 buffer [len < 511 ? len : 511] = 0;
368 b = buffer;
369 for (i = 0; i < 3; i++) {
370 if (kind == i)
371 return strtod (b, NULL);
372 if (i < 2) {
373 b = strchr (b, ' ');
374 if (!b)
375 return 0;
376 b += 1;
381 #endif
382 return 0;
385 static double
386 cpu_load_1min (void)
388 return cpu_load (0);
391 static double
392 cpu_load_5min (void)
394 return cpu_load (1);
397 static double
398 cpu_load_15min (void)
400 return cpu_load (2);
403 #define SYSCOUNTER_TIME (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
404 #define SYSCOUNTER_BYTES (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
405 #define SYSCOUNTER_COUNT (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_COUNT | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
406 #define SYSCOUNTER_LOAD (MONO_COUNTER_SYSTEM | MONO_COUNTER_DOUBLE | MONO_COUNTER_PERCENTAGE | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
408 static void
409 initialize_system_counters (void)
411 register_internal ("User Time", SYSCOUNTER_TIME, (gpointer) &user_time, sizeof (gint64));
412 register_internal ("System Time", SYSCOUNTER_TIME, (gpointer) &system_time, sizeof (gint64));
413 register_internal ("Total Time", SYSCOUNTER_TIME, (gpointer) &total_time, sizeof (gint64));
414 register_internal ("Working Set", SYSCOUNTER_BYTES, (gpointer) &working_set, sizeof (gint64));
415 register_internal ("Private Bytes", SYSCOUNTER_BYTES, (gpointer) &private_bytes, sizeof (gint64));
416 register_internal ("Virtual Bytes", SYSCOUNTER_BYTES, (gpointer) &virtual_bytes, sizeof (gint64));
417 register_internal ("Page File Bytes", SYSCOUNTER_BYTES, (gpointer) &paged_bytes, sizeof (gint64));
418 register_internal ("Page Faults", SYSCOUNTER_COUNT, (gpointer) &page_faults, sizeof (gint64));
419 register_internal ("CPU Load Average - 1min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_1min, sizeof (double));
420 register_internal ("CPU Load Average - 5min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_5min, sizeof (double));
421 register_internal ("CPU Load Average - 15min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_15min, sizeof (double));
425 * mono_counters_foreach:
426 * \param cb The callback that will be called for each counter.
427 * \param user_data Value passed as second argument of the callback.
428 * Iterate over all counters and call \p cb for each one of them. Stop iterating if
429 * the callback returns FALSE.
431 void
432 mono_counters_foreach (CountersEnumCallback cb, gpointer user_data)
434 MonoCounter *counter;
436 if (!initialized) {
437 g_debug ("counters not enabled");
438 return;
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);
446 return;
450 mono_os_mutex_unlock (&counters_mutex);
453 #define COPY_COUNTER(type,functype) do { \
454 size = sizeof (type); \
455 if (buffer_size < size) \
456 size = -1; \
457 else \
458 *(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
459 } while (0);
461 /* lockless */
462 static int
463 sample_internal (MonoCounter *counter, void *buffer, int buffer_size)
465 int cb = counter->type & MONO_COUNTER_CALLBACK;
466 int size = -1;
468 char *strval;
470 switch (mono_counter_get_type (counter)) {
471 case MONO_COUNTER_INT:
472 COPY_COUNTER (int, IntFunc);
473 break;
474 case MONO_COUNTER_UINT:
475 COPY_COUNTER (guint, UIntFunc);
476 break;
477 case MONO_COUNTER_LONG:
478 case MONO_COUNTER_TIME_INTERVAL:
479 COPY_COUNTER (gint64, LongFunc);
480 break;
481 case MONO_COUNTER_ULONG:
482 COPY_COUNTER (guint64, ULongFunc);
483 break;
484 case MONO_COUNTER_WORD:
485 COPY_COUNTER (gssize, PtrFunc);
486 break;
487 case MONO_COUNTER_DOUBLE:
488 COPY_COUNTER (double, DoubleFunc);
489 break;
490 case MONO_COUNTER_STRING:
491 if (buffer_size < counter->size) {
492 size = -1;
493 } else if (counter->size == 0) {
494 size = 0;
495 } else {
496 strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
497 if (!strval) {
498 size = 0;
499 } else {
500 size = counter->size;
501 memcpy ((char *) buffer, strval, size - 1);
502 ((char*)buffer)[size - 1] = '\0';
507 return size;
511 mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
513 if (!initialized) {
514 g_debug ("counters not enabled");
515 return -1;
518 return sample_internal (counter, buffer, buffer_size);
521 #define ENTRY_FMT "%-36s: "
522 static void
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);
530 break;
531 case MONO_COUNTER_UINT:
532 fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
533 break;
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);
537 else
538 fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(long long *)buffer);
539 break;
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);
543 else
544 fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(unsigned long long *)buffer);
545 break;
546 case MONO_COUNTER_WORD:
547 fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, (long long)*(gssize*)buffer);
548 break;
549 case MONO_COUNTER_DOUBLE:
550 fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
551 break;
552 case MONO_COUNTER_STRING:
553 fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (size == 0) ? "(null)" : (char*)buffer);
554 break;
555 case MONO_COUNTER_TIME_INTERVAL:
556 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
557 break;
560 g_free (buffer);
563 static const char
564 section_names [][12] = {
565 "JIT",
566 "GC",
567 "Metadata",
568 "Generics",
569 "Security",
570 "Runtime",
571 "System",
572 "", // MONO_COUNTER_PERFCOUNTERS - not used.
573 "Profiler",
574 "Interp",
577 static void
578 mono_counters_dump_section (int section, int variance, FILE *outfile)
580 MonoCounter *counter = counters;
581 while (counter) {
582 if ((counter->type & section) && (mono_counter_get_variance (counter) & variance))
583 dump_counter (counter, outfile);
584 counter = counter->next;
589 * mono_counters_dump:
590 * \param section_mask The sections to dump counters for
591 * \param 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 \c MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance.
596 void
597 mono_counters_dump (int section_mask, FILE *outfile)
599 int i, j;
600 int variance;
601 section_mask &= valid_mask;
603 if (!initialized)
604 return;
606 mono_os_mutex_lock (&counters_mutex);
608 if (!counters) {
609 mono_os_mutex_unlock (&counters_mutex);
610 return;
613 variance = section_mask & MONO_COUNTER_VARIANCE_MASK;
615 /* If no variance mask is supplied, we default to all kinds. */
616 if (!variance)
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);
627 fflush (outfile);
628 mono_os_mutex_unlock (&counters_mutex);
632 * mono_counters_cleanup:
634 * Perform any needed cleanup at process exit.
636 void
637 mono_counters_cleanup (void)
639 MonoCounter *counter;
641 if (!initialized)
642 return;
644 mono_os_mutex_lock (&counters_mutex);
646 counter = counters;
647 counters = NULL;
648 while (counter) {
649 MonoCounter *tmp = counter;
650 counter = counter->next;
651 g_free ((void*)tmp->name);
652 g_free (tmp);
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 * \param resource_type one of the \c MonoResourceType enum values
664 * \param value the current value of the resource usage
665 * Check if a runtime resource limit has been reached. This function
666 * is intended to be used by the runtime only.
668 void
669 mono_runtime_resource_check_limit (int resource_type, uintptr_t value)
671 if (!limit_reached)
672 return;
673 /* check the hard limit first */
674 if (value > resource_limits [resource_type * 2 + 1]) {
675 limit_reached (resource_type, value, 0);
676 return;
678 if (value > resource_limits [resource_type * 2])
679 limit_reached (resource_type, value, 1);
683 * mono_runtime_resource_limit:
684 * \param resource_type one of the \c MonoResourceType enum values
685 * \param soft_limit the soft limit value
686 * \param hard_limit the hard limit value
687 * This function sets the soft and hard limit for runtime resources. When the limit
688 * is reached, a user-specified callback is called. The callback runs in a restricted
689 * environment, in which the world coult be stopped, so it can't take locks, perform
690 * allocations etc. The callback may be called multiple times once a limit has been reached
691 * if action is not taken to decrease the resource use.
692 * \returns 0 on error or a positive integer otherwise.
695 mono_runtime_resource_limit (int resource_type, uintptr_t soft_limit, uintptr_t hard_limit)
697 if (resource_type >= MONO_RESOURCE_COUNT || resource_type < 0)
698 return 0;
699 if (soft_limit > hard_limit)
700 return 0;
701 resource_limits [resource_type * 2] = soft_limit;
702 resource_limits [resource_type * 2 + 1] = hard_limit;
703 return 1;
707 * mono_runtime_resource_set_callback:
708 * \param callback a function pointer
709 * Set the callback to be invoked when a resource limit is reached.
710 * The callback will receive the resource type, the resource amount in resource-specific
711 * units and a flag indicating whether the soft or hard limit was reached.
713 void
714 mono_runtime_resource_set_callback (MonoResourceCallback callback)
716 limit_reached = callback;