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  */
7 
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"
14 
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 
19 struct _MonoCounter {
20 	MonoCounter *next;
21 	const char *name;
22 	void *addr;
23 	int type;
24 	size_t size;
25 };
26 
27 static MonoCounter *counters = NULL;
28 static mono_mutex_t counters_mutex;
29 
30 static volatile gboolean initialized = FALSE;
31 
32 static int valid_mask = 0;
33 static int set_mask = 0;
34 
35 static GSList *register_callbacks = NULL;
36 
37 static void initialize_system_counters (void);
38 
39 /**
40  * mono_counter_get_variance:
41  * \param counter counter to get the variance
42  *
43  * Variance specifies how the counter value is expected to behave between any two samplings.
44  *
45  * \returns the monotonicity of the counter.
46  */
47 int
mono_counter_get_variance(MonoCounter * counter)48 mono_counter_get_variance (MonoCounter *counter)
49 {
50 	return counter->type & MONO_COUNTER_VARIANCE_MASK;
51 }
52 
53 /**
54  * mono_counter_get_unit:
55  * \param counter counter to get the unit
56  *
57  * The unit gives a high level view of the unit that the counter is measuring.
58  *
59  * \returns the unit of the counter.
60  */
61 int
mono_counter_get_unit(MonoCounter * counter)62 mono_counter_get_unit (MonoCounter *counter)
63 {
64 	return counter->type & MONO_COUNTER_UNIT_MASK;
65 }
66 
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.
72  */
73 
74 int
mono_counter_get_section(MonoCounter * counter)75 mono_counter_get_section (MonoCounter *counter)
76 {
77 	return counter->type & MONO_COUNTER_SECTION_MASK;
78 }
79 
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.
84  */
85 int
mono_counter_get_type(MonoCounter * counter)86 mono_counter_get_type (MonoCounter *counter)
87 {
88 	return counter->type & MONO_COUNTER_TYPE_MASK;
89 }
90 
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.
95  */
96 
97 const char*
mono_counter_get_name(MonoCounter * counter)98 mono_counter_get_name (MonoCounter *counter)
99 {
100 	return counter->name;
101 }
102 
103 /**
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.
108  */
109 size_t
mono_counter_get_size(MonoCounter * counter)110 mono_counter_get_size (MonoCounter *counter)
111 {
112 	return counter->size;
113 }
114 
115 /**
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.
119  */
120 void
mono_counters_enable(int section_mask)121 mono_counters_enable (int section_mask)
122 {
123 	valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
124 }
125 
126 void
mono_counters_init(void)127 mono_counters_init (void)
128 {
129 	if (initialized)
130 		return;
131 
132 	mono_os_mutex_init (&counters_mutex);
133 
134 	initialize_system_counters ();
135 
136 	initialized = TRUE;
137 }
138 
139 static void
register_internal(const char * name,int type,void * addr,int size)140 register_internal (const char *name, int type, void *addr, int size)
141 {
142 	MonoCounter *counter;
143 	GSList *register_callback;
144 
145 	g_assert (size >= 0);
146 	if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
147 		type |= MONO_COUNTER_MONOTONIC;
148 
149 	mono_os_mutex_lock (&counters_mutex);
150 
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;
156 		}
157 	}
158 
159 	counter = (MonoCounter *) g_malloc (sizeof (MonoCounter));
160 	if (!counter) {
161 		mono_os_mutex_unlock (&counters_mutex);
162 		return;
163 	}
164 	counter->name = g_strdup (name);
165 	counter->type = type;
166 	counter->addr = addr;
167 	counter->next = NULL;
168 	counter->size = size;
169 
170 	set_mask |= type;
171 
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;
180 	}
181 
182 	for (register_callback = register_callbacks; register_callback; register_callback = register_callback->next)
183 		((MonoCounterRegisterCallback)register_callback->data) (counter);
184 
185 	mono_os_mutex_unlock (&counters_mutex);
186 }
187 
188 /**
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.
193  *
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.
197  *
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.
200  *
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.
203  */
204 void
mono_counters_register(const char * name,int type,void * addr)205 mono_counters_register (const char* name, int type, void *addr)
206 {
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 ();
233 	}
234 
235 	if (!initialized)
236 		g_debug ("counters not enabled");
237 	else
238 		register_internal (name, type, addr, size);
239 }
240 
241 /**
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.
247  *
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.
251  *
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.
254  *
255  * The value of \p size is ignored for types with fixed size such as int and long.
256  *
257  * Use \p size for types that can have dynamic size such as string.
258  *
259  * If \p size is negative, it's silently converted to zero.
260  */
261 void
mono_counters_register_with_size(const char * name,int type,void * addr,int size)262 mono_counters_register_with_size (const char *name, int type, void *addr, int size)
263 {
264 	if (!initialized)
265 		g_debug ("counters not enabled");
266 	else
267 		register_internal (name, type, addr, size);
268 }
269 
270 /**
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
274  */
275 void
mono_counters_on_register(MonoCounterRegisterCallback callback)276 mono_counters_on_register (MonoCounterRegisterCallback callback)
277 {
278 	if (!initialized) {
279 		g_debug ("counters not enabled");
280 		return;
281 	}
282 
283 	mono_os_mutex_lock (&counters_mutex);
284 	register_callbacks = g_slist_append (register_callbacks, (gpointer) callback);
285 	mono_os_mutex_unlock (&counters_mutex);
286 }
287 
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);
295 
296 static gint64
user_time(void)297 user_time (void)
298 {
299 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
300 }
301 
302 static gint64
system_time(void)303 system_time (void)
304 {
305 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
306 }
307 
308 static gint64
total_time(void)309 total_time (void)
310 {
311 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
312 }
313 
314 static gint64
working_set(void)315 working_set (void)
316 {
317 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
318 }
319 
320 static gint64
private_bytes(void)321 private_bytes (void)
322 {
323 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
324 }
325 
326 static gint64
virtual_bytes(void)327 virtual_bytes (void)
328 {
329 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
330 }
331 
332 static gint64
page_faults(void)333 page_faults (void)
334 {
335 	return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS);
336 }
337 
338 
339 // 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
340 // cause a failure when registering counters since the same function address will be used by all three functions. Preventing this method from being inlined
341 // will make sure the registered callback functions remains unique.
342 #ifdef _MSC_VER
343 __declspec(noinline)
344 #endif
345 static double
cpu_load(int kind)346 cpu_load (int kind)
347 {
348 #if defined(TARGET_WIN32)
349 #elif defined(TARGET_MACH)
350 	double load [3];
351 	if (getloadavg (load, 3) > 0)
352 		return load [kind];
353 #else
354 	char buffer[512], *b;
355 	int len, i;
356 	FILE *f = fopen ("/proc/loadavg", "r");
357 	if (f) {
358 		len = fread (buffer, 1, sizeof (buffer) - 1, f);
359 		fclose (f);
360 		if (len > 0) {
361 			buffer [len < 511 ? len : 511] = 0;
362 			b = buffer;
363 			for (i = 0; i < 3; i++) {
364 				if (kind == i)
365 					return strtod (b, NULL);
366 				if (i < 2) {
367 					b = strchr (b, ' ');
368 					if (!b)
369 						return 0;
370 					b += 1;
371 				}
372 			}
373 		}
374 	}
375 #endif
376 	return 0;
377 }
378 
379 static double
cpu_load_1min(void)380 cpu_load_1min (void)
381 {
382 	return cpu_load (0);
383 }
384 
385 static double
cpu_load_5min(void)386 cpu_load_5min (void)
387 {
388 	return cpu_load (1);
389 }
390 
391 static double
cpu_load_15min(void)392 cpu_load_15min (void)
393 {
394 	return cpu_load (2);
395 }
396 
397 #define SYSCOUNTER_TIME (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
398 #define SYSCOUNTER_BYTES (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
399 #define SYSCOUNTER_COUNT (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_COUNT | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
400 #define SYSCOUNTER_LOAD (MONO_COUNTER_SYSTEM | MONO_COUNTER_DOUBLE | MONO_COUNTER_PERCENTAGE | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
401 
402 static void
initialize_system_counters(void)403 initialize_system_counters (void)
404 {
405 	register_internal ("User Time", SYSCOUNTER_TIME, (gpointer) &user_time, sizeof (gint64));
406 	register_internal ("System Time", SYSCOUNTER_TIME, (gpointer) &system_time, sizeof (gint64));
407 	register_internal ("Total Time", SYSCOUNTER_TIME, (gpointer) &total_time, sizeof (gint64));
408 	register_internal ("Working Set", SYSCOUNTER_BYTES, (gpointer) &working_set, sizeof (gint64));
409 	register_internal ("Private Bytes", SYSCOUNTER_BYTES, (gpointer) &private_bytes, sizeof (gint64));
410 	register_internal ("Virtual Bytes", SYSCOUNTER_BYTES, (gpointer) &virtual_bytes, sizeof (gint64));
411 	register_internal ("Page Faults", SYSCOUNTER_COUNT, (gpointer) &page_faults, sizeof (gint64));
412 	register_internal ("CPU Load Average - 1min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_1min, sizeof (double));
413 	register_internal ("CPU Load Average - 5min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_5min, sizeof (double));
414 	register_internal ("CPU Load Average - 15min", SYSCOUNTER_LOAD, (gpointer) &cpu_load_15min, sizeof (double));
415 }
416 
417 /**
418  * mono_counters_foreach:
419  * \param cb The callback that will be called for each counter.
420  * \param user_data Value passed as second argument of the callback.
421  * Iterate over all counters and call \p cb for each one of them. Stop iterating if
422  * the callback returns FALSE.
423  */
424 void
mono_counters_foreach(CountersEnumCallback cb,gpointer user_data)425 mono_counters_foreach (CountersEnumCallback cb, gpointer user_data)
426 {
427 	MonoCounter *counter;
428 
429 	if (!initialized) {
430 		g_debug ("counters not enabled");
431 		return;
432 	}
433 
434 	mono_os_mutex_lock (&counters_mutex);
435 
436 	for (counter = counters; counter; counter = counter->next) {
437 		if (!cb (counter, user_data)) {
438 			mono_os_mutex_unlock (&counters_mutex);
439 			return;
440 		}
441 	}
442 
443 	mono_os_mutex_unlock (&counters_mutex);
444 }
445 
446 #define COPY_COUNTER(type,functype) do {	\
447 		size = sizeof (type);	\
448 		if (buffer_size < size)	\
449 			size = -1;	\
450 		else			\
451 			*(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
452 	} while (0);
453 
454 /* lockless */
455 static int
sample_internal(MonoCounter * counter,void * buffer,int buffer_size)456 sample_internal (MonoCounter *counter, void *buffer, int buffer_size)
457 {
458 	int cb = counter->type & MONO_COUNTER_CALLBACK;
459 	int size = -1;
460 
461 	char *strval;
462 
463 	switch (mono_counter_get_type (counter)) {
464 	case MONO_COUNTER_INT:
465 		COPY_COUNTER (int, IntFunc);
466 		break;
467 	case MONO_COUNTER_UINT:
468 		COPY_COUNTER (guint, UIntFunc);
469 		break;
470 	case MONO_COUNTER_LONG:
471 	case MONO_COUNTER_TIME_INTERVAL:
472 		COPY_COUNTER (gint64, LongFunc);
473 		break;
474 	case MONO_COUNTER_ULONG:
475 		COPY_COUNTER (guint64, ULongFunc);
476 		break;
477 	case MONO_COUNTER_WORD:
478 		COPY_COUNTER (gssize, PtrFunc);
479 		break;
480 	case MONO_COUNTER_DOUBLE:
481 		COPY_COUNTER (double, DoubleFunc);
482 		break;
483 	case MONO_COUNTER_STRING:
484 		if (buffer_size < counter->size) {
485 			size = -1;
486 		} else if (counter->size == 0) {
487 			size = 0;
488 		} else {
489 			strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
490 			if (!strval) {
491 				size = 0;
492 			} else {
493 				size = counter->size;
494 				memcpy ((char *) buffer, strval, size - 1);
495 				((char*)buffer)[size - 1] = '\0';
496 			}
497 		}
498 	}
499 
500 	return size;
501 }
502 
503 int
mono_counters_sample(MonoCounter * counter,void * buffer,int buffer_size)504 mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
505 {
506 	if (!initialized) {
507 		g_debug ("counters not enabled");
508 		return -1;
509 	}
510 
511 	return sample_internal (counter, buffer, buffer_size);
512 }
513 
514 #define ENTRY_FMT "%-36s: "
515 static void
dump_counter(MonoCounter * counter,FILE * outfile)516 dump_counter (MonoCounter *counter, FILE *outfile) {
517 	void *buffer = g_malloc0 (counter->size);
518 	int size = sample_internal (counter, buffer, counter->size);
519 
520 	switch (counter->type & MONO_COUNTER_TYPE_MASK) {
521 	case MONO_COUNTER_INT:
522 		fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer);
523 		break;
524 	case MONO_COUNTER_UINT:
525 		fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
526 		break;
527 	case MONO_COUNTER_LONG:
528 		if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
529 			fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 10000.0);
530 		else
531 			fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(long long *)buffer);
532 		break;
533 	case MONO_COUNTER_ULONG:
534 		if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
535 			fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(guint64*)buffer) / 10000.0);
536 		else
537 			fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(unsigned long long *)buffer);
538 		break;
539 	case MONO_COUNTER_WORD:
540 		fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
541 		break;
542 	case MONO_COUNTER_DOUBLE:
543 		fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
544 		break;
545 	case MONO_COUNTER_STRING:
546 		fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (size == 0) ? "(null)" : (char*)buffer);
547 		break;
548 	case MONO_COUNTER_TIME_INTERVAL:
549 		fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
550 		break;
551 	}
552 
553 	g_free (buffer);
554 }
555 
556 static const char
557 section_names [][12] = {
558 	"JIT",
559 	"GC",
560 	"Metadata",
561 	"Generics",
562 	"Security",
563 	"Runtime",
564 	"System",
565 	"", // MONO_COUNTER_PERFCOUNTERS - not used.
566 	"Profiler",
567 };
568 
569 static void
mono_counters_dump_section(int section,int variance,FILE * outfile)570 mono_counters_dump_section (int section, int variance, FILE *outfile)
571 {
572 	MonoCounter *counter = counters;
573 	while (counter) {
574 		if ((counter->type & section) && (mono_counter_get_variance (counter) & variance))
575 			dump_counter (counter, outfile);
576 		counter = counter->next;
577 	}
578 }
579 
580 /**
581  * mono_counters_dump:
582  * \param section_mask The sections to dump counters for
583  * \param outfile a FILE to dump the results to
584  * Displays the counts of all the enabled counters registered.
585  * To filter by variance, you can OR one or more variance with the specific section you want.
586  * Use \c MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance.
587  */
588 void
mono_counters_dump(int section_mask,FILE * outfile)589 mono_counters_dump (int section_mask, FILE *outfile)
590 {
591 	int i, j;
592 	int variance;
593 	section_mask &= valid_mask;
594 
595 	if (!initialized)
596 		return;
597 
598 	mono_os_mutex_lock (&counters_mutex);
599 
600 	if (!counters) {
601 		mono_os_mutex_unlock (&counters_mutex);
602 		return;
603 	}
604 
605 	variance = section_mask & MONO_COUNTER_VARIANCE_MASK;
606 
607 	/* If no variance mask is supplied, we default to all kinds. */
608 	if (!variance)
609 		variance = MONO_COUNTER_VARIANCE_MASK;
610 	section_mask &= ~MONO_COUNTER_VARIANCE_MASK;
611 
612 	for (j = 0, i = MONO_COUNTER_JIT; i < MONO_COUNTER_LAST_SECTION; j++, i <<= 1) {
613 		if ((section_mask & i) && (set_mask & i)) {
614 			fprintf (outfile, "\n%s statistics\n", section_names [j]);
615 			mono_counters_dump_section (i, variance, outfile);
616 		}
617 	}
618 
619 	fflush (outfile);
620 	mono_os_mutex_unlock (&counters_mutex);
621 }
622 
623 /**
624  * mono_counters_cleanup:
625  *
626  * Perform any needed cleanup at process exit.
627  */
628 void
mono_counters_cleanup(void)629 mono_counters_cleanup (void)
630 {
631 	MonoCounter *counter;
632 
633 	if (!initialized)
634 		return;
635 
636 	mono_os_mutex_lock (&counters_mutex);
637 
638 	counter = counters;
639 	counters = NULL;
640 	while (counter) {
641 		MonoCounter *tmp = counter;
642 		counter = counter->next;
643 		g_free ((void*)tmp->name);
644 		g_free (tmp);
645 	}
646 
647 	mono_os_mutex_unlock (&counters_mutex);
648 }
649 
650 static MonoResourceCallback limit_reached = NULL;
651 static uintptr_t resource_limits [MONO_RESOURCE_COUNT * 2];
652 
653 /**
654  * mono_runtime_resource_check_limit:
655  * \param resource_type one of the \c MonoResourceType enum values
656  * \param value the current value of the resource usage
657  * Check if a runtime resource limit has been reached. This function
658  * is intended to be used by the runtime only.
659  */
660 void
mono_runtime_resource_check_limit(int resource_type,uintptr_t value)661 mono_runtime_resource_check_limit (int resource_type, uintptr_t value)
662 {
663 	if (!limit_reached)
664 		return;
665 	/* check the hard limit first */
666 	if (value > resource_limits [resource_type * 2 + 1]) {
667 		limit_reached (resource_type, value, 0);
668 		return;
669 	}
670 	if (value > resource_limits [resource_type * 2])
671 		limit_reached (resource_type, value, 1);
672 }
673 
674 /**
675  * mono_runtime_resource_limit:
676  * \param resource_type one of the \c MonoResourceType enum values
677  * \param soft_limit the soft limit value
678  * \param hard_limit the hard limit value
679  * This function sets the soft and hard limit for runtime resources. When the limit
680  * is reached, a user-specified callback is called. The callback runs in a restricted
681  * environment, in which the world coult be stopped, so it can't take locks, perform
682  * allocations etc. The callback may be called multiple times once a limit has been reached
683  * if action is not taken to decrease the resource use.
684  * \returns 0 on error or a positive integer otherwise.
685  */
686 int
mono_runtime_resource_limit(int resource_type,uintptr_t soft_limit,uintptr_t hard_limit)687 mono_runtime_resource_limit (int resource_type, uintptr_t soft_limit, uintptr_t hard_limit)
688 {
689 	if (resource_type >= MONO_RESOURCE_COUNT || resource_type < 0)
690 		return 0;
691 	if (soft_limit > hard_limit)
692 		return 0;
693 	resource_limits [resource_type * 2] = soft_limit;
694 	resource_limits [resource_type * 2 + 1] = hard_limit;
695 	return 1;
696 }
697 
698 /**
699  * mono_runtime_resource_set_callback:
700  * \param callback a function pointer
701  * Set the callback to be invoked when a resource limit is reached.
702  * The callback will receive the resource type, the resource amount in resource-specific
703  * units and a flag indicating whether the soft or hard limit was reached.
704  */
705 void
mono_runtime_resource_set_callback(MonoResourceCallback callback)706 mono_runtime_resource_set_callback (MonoResourceCallback callback)
707 {
708 	limit_reached = callback;
709 }
710 
711 
712