Merge pull request #1079 from esdrubal/webclient_cancel
[mono.git] / mono / utils / mono-counters.c
1 /*
2  * Copyright 2006-2010 Novell
3  * Copyright 2011 Xamarin Inc
4  */
5
6 #include <stdlib.h>
7 #include <glib.h>
8 #include "config.h"
9 #include "mono-counters.h"
10 #include "mono-proclib.h"
11
12 #ifdef HAVE_UNISTD_H
13 #include <unistd.h>
14 #endif
15
16 struct _MonoCounter {
17         MonoCounter *next;
18         const char *name;
19         void *addr;
20         int type;
21         size_t size;
22 };
23
24 static MonoCounter *counters = NULL;
25 static int valid_mask = 0;
26 static int set_mask = 0;
27
28 /**
29  * mono_counter_get_variance:
30  * @counter: counter to get the variance
31  *
32  * Variance specifies how the counter value is expected to behave between any two samplings.
33  *
34  * Returns: the monotonicity of the counter.
35  */
36 int
37 mono_counter_get_variance (MonoCounter *counter)
38 {
39         return counter->type & MONO_COUNTER_VARIANCE_MASK;
40 }
41
42 /**
43  * mono_counter_get_unit:
44  * @counter: counter to get the unit
45  *
46  * The unit gives a high level view of the unit that the counter is measuring.
47  *
48  * Returns: the unit of the counter.
49  */
50 int
51 mono_counter_get_unit (MonoCounter *counter)
52 {
53         return counter->type & MONO_COUNTER_UNIT_MASK;
54 }
55
56 /**
57  * mono_counter_get_section:
58  * @counter: counter to get the section
59  *
60  * Sections are the unit of organization between all counters.
61  *
62  * Returns: the section of the counter.
63  */
64
65 int
66 mono_counter_get_section (MonoCounter *counter)
67 {
68         return counter->type & MONO_COUNTER_SECTION_MASK;
69 }
70
71 /**
72  * mono_counter_get_type:
73  * @counter: counter to get the type
74  *
75  * Returns the type used to strong the value of the counter.
76  *
77  * Returns:the type of the counter.
78  */
79 int
80 mono_counter_get_type (MonoCounter *counter)
81 {
82         return counter->type & MONO_COUNTER_TYPE_MASK;
83 }
84
85 /**
86  * mono_counter_get_name:
87  * @counter: counter to get the name
88  *
89  * Returns the counter name. The string should not be freed.
90  *
91  * Returns the name of the counter.
92  */
93
94 const char*
95 mono_counter_get_name (MonoCounter *counter)
96 {
97         return counter->name;
98 }
99
100 /**
101  * mono_counter_get_size:
102  * @counter: counter to get the max size of the counter
103  *
104  * Use the returned size to create the buffer used with mono_counters_sample
105  *
106  * Returns: the max size of the counter data.
107  */
108 size_t
109 mono_counter_get_size (MonoCounter *counter)
110 {
111         return counter->size;
112 }
113
114 /**
115  * mono_counters_enable:
116  * @section_mask: a mask listing the sections that will be displayed
117  *
118  * This is used to track which counters will be displayed.
119  */
120 void
121 mono_counters_enable (int section_mask)
122 {
123         valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
124 }
125
126 /**
127  * mono_counters_register:
128  * @name: The name for this counters.
129  * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
130  * @addr: The address to register.
131  *
132  * Register addr as the address of a counter of type type.
133  * Note that @name must be a valid string at all times until
134  * mono_counters_dump () is called.
135  *
136  * This function should not be used with counter types that require an explicit size such as string
137  * as the counter size will be set to zero making them effectively useless.
138  *
139  *
140  * It may be a function pointer if MONO_COUNTER_CALLBACK is specified:
141  * the function should return the value and take no arguments.
142  */
143 void 
144 mono_counters_register (const char* name, int type, void *addr)
145 {
146         int size;
147         switch (type & MONO_COUNTER_TYPE_MASK) {
148         case MONO_COUNTER_INT:
149                 size = sizeof (int);
150                 break;
151         case MONO_COUNTER_UINT:
152                 size = sizeof (guint);
153                 break;
154         case MONO_COUNTER_LONG:
155         case MONO_COUNTER_TIME_INTERVAL:
156                 size = sizeof (gint64);
157                 break;
158         case MONO_COUNTER_ULONG:
159                 size = sizeof (guint64);
160                 break;
161         case MONO_COUNTER_WORD:
162                 size = sizeof (gssize);
163                 break;
164         case MONO_COUNTER_DOUBLE:
165                 size = sizeof (double);
166                 break;
167         case MONO_COUNTER_STRING:
168                 size = 0;
169                 break;
170         default:
171                 g_assert_not_reached ();
172         }
173         mono_counters_register_with_size (name, type, addr, size);
174 }
175
176 /**
177  * mono_counters_register_with_size:
178  * @name: The name for this counters.
179  * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
180  * @addr: The address to register.
181  * @size: Max size of the counter data.
182  *
183  * Register addr as the address of a counter of type @type.
184  * Note that @name must be a valid string at all times until
185  * mono_counters_dump () is called.
186  *
187  * It may be a function pointer if MONO_COUNTER_CALLBACK is specified:
188  * the function should return the value and take no arguments.
189  *
190  * The value of @size is ignored for types with fixed size such as int and long.
191  *
192  * Use @size for types that can have dynamic size such as string.
193  *
194  * If @size is negative, it's silently converted to zero.
195  */
196 void
197 mono_counters_register_with_size (const char *name, int type, void *addr, int size)
198 {
199         MonoCounter *counter;
200
201         if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
202                 type |= MONO_COUNTER_MONOTONIC;
203         if (size < 0)
204                 size = 0;
205
206         counter = malloc (sizeof (MonoCounter));
207         if (!counter)
208                 return;
209         counter->name = name;
210         counter->type = type;
211         counter->addr = addr;
212         counter->next = NULL;
213         counter->size = size;
214
215         set_mask |= type;
216
217         /* Append */
218         if (counters) {
219                 MonoCounter *item = counters;
220                 while (item->next)
221                         item = item->next;
222                 item->next = counter;
223         } else {
224                 counters = counter;
225         }
226 }
227
228 typedef int (*IntFunc) (void);
229 typedef guint (*UIntFunc) (void);
230 typedef gint64 (*LongFunc) (void);
231 typedef guint64 (*ULongFunc) (void);
232 typedef gssize (*PtrFunc) (void);
233 typedef double (*DoubleFunc) (void);
234 typedef char* (*StrFunc) (void);
235
236 #define ENTRY_FMT "%-36s: "
237 static void
238 dump_counter (MonoCounter *counter, FILE *outfile) {
239         void *buffer = g_malloc0 (counter->size);
240         mono_counters_sample (counter, buffer, counter->size);
241
242         switch (counter->type & MONO_COUNTER_TYPE_MASK) {
243         case MONO_COUNTER_INT:
244                 fprintf (outfile, ENTRY_FMT "%d\n", counter->name, *(int*)buffer);
245                 break;
246         case MONO_COUNTER_UINT:
247                 fprintf (outfile, ENTRY_FMT "%u\n", counter->name, *(guint*)buffer);
248                 break;
249         case MONO_COUNTER_LONG:
250                 if ((counter->type & MONO_COUNTER_UNIT_MASK) == MONO_COUNTER_TIME)
251                         fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 10000.0);
252                 else
253                         fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, *(gint64*)buffer);
254                 break;
255         case MONO_COUNTER_ULONG:
256                 fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, *(guint64*)buffer);
257                 break;
258         case MONO_COUNTER_WORD:
259                 fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, *(gssize*)buffer);
260                 break;
261         case MONO_COUNTER_DOUBLE:
262                 fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, *(double*)buffer);
263                 break;
264         case MONO_COUNTER_STRING:
265                 fprintf (outfile, ENTRY_FMT "%s\n", counter->name, (counter->size == 0) ? "(null)" : (char*)buffer);
266                 break;
267         case MONO_COUNTER_TIME_INTERVAL:
268                 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)(*(gint64*)buffer) / 1000.0);
269                 break;
270         }
271
272         g_free (buffer);
273 }
274
275 static gint64
276 user_time (void)
277 {
278         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_USER_TIME);
279 }
280
281 static gint64
282 system_time (void)
283 {
284         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_SYSTEM_TIME);
285 }
286
287 static gint64
288 total_time (void)
289 {
290         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_TOTAL_TIME);
291 }
292
293 static gint64
294 working_set (void)
295 {
296         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_WORKING_SET);
297 }
298
299 static gint64
300 private_bytes (void)
301 {
302         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_PRIVATE_BYTES);
303 }
304
305 static gint64
306 virtual_bytes (void)
307 {
308         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_VIRTUAL_BYTES);
309 }
310
311 static gint64
312 page_faults (void)
313 {
314         return mono_process_get_data (GINT_TO_POINTER (mono_process_current_pid ()), MONO_PROCESS_FAULTS);
315 }
316
317 static double
318 cpu_load (int kind)
319 {
320 #if defined(TARGET_WIN32)
321 #elif defined(TARGET_MACH)
322         double load [3];
323         if (getloadavg (load, 3) > 0)
324                 return load [kind];
325 #else
326         char buffer[512], *b;
327         int len, i;
328         FILE *f = fopen ("/proc/loadavg", "r");
329         if (f) {
330                 len = fread (buffer, 1, sizeof (buffer) - 1, f);
331                 if (len > 0) {
332                         buffer [len < 511 ? len : 511] = 0;
333                         b = buffer;
334                         for (i = 0; i < 3; i++) {
335                                 if (kind == i)
336                                         return strtod (b, NULL);
337                                 if (i < 2) {
338                                         b = strchr (b, ' ');
339                                         if (!b)
340                                                 return 0;
341                                         b += 1;
342                                 }
343                         }
344                 }
345                 fclose (f);
346         }
347 #endif
348         return 0;
349 }
350
351 static double
352 cpu_load_1min (void)
353 {
354         return cpu_load (0);
355 }
356
357 static double
358 cpu_load_5min (void)
359 {
360         return cpu_load (1);
361 }
362
363 static double
364 cpu_load_15min (void)
365 {
366         return cpu_load (2);
367 }
368
369 static gboolean system_counters_initialized = FALSE;
370
371 #define SYSCOUNTER_TIME (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
372 #define SYSCOUNTER_BYTES (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
373 #define SYSCOUNTER_COUNT (MONO_COUNTER_SYSTEM | MONO_COUNTER_LONG | MONO_COUNTER_COUNT | MONO_COUNTER_MONOTONIC | MONO_COUNTER_CALLBACK)
374 #define SYSCOUNTER_LOAD (MONO_COUNTER_SYSTEM | MONO_COUNTER_DOUBLE | MONO_COUNTER_PERCENTAGE | MONO_COUNTER_VARIABLE | MONO_COUNTER_CALLBACK)
375
376 static void
377 initialize_system_counters (void)
378 {
379         mono_counters_register ("User Time", SYSCOUNTER_TIME, &user_time);
380         mono_counters_register ("System Time", SYSCOUNTER_TIME, &system_time);
381         mono_counters_register ("Total Time", SYSCOUNTER_TIME, &total_time);
382         mono_counters_register ("Working Set", SYSCOUNTER_BYTES, &working_set);
383         mono_counters_register ("Private Bytes", SYSCOUNTER_BYTES, &private_bytes);
384         mono_counters_register ("Virtual Bytes", SYSCOUNTER_BYTES, &virtual_bytes);
385         mono_counters_register ("Page Faults", SYSCOUNTER_COUNT, &page_faults);
386         mono_counters_register ("CPU Load Average - 1min", SYSCOUNTER_LOAD, &cpu_load_1min);
387         mono_counters_register ("CPU Load Average - 5min", SYSCOUNTER_LOAD, &cpu_load_5min);
388         mono_counters_register ("CPU Load Average - 15min", SYSCOUNTER_LOAD, &cpu_load_15min);
389
390         system_counters_initialized = TRUE;
391 }
392
393 /**
394  * mono_counters_foreach:
395  * @cb: The callback that will be called for each counter.
396  * @user_data: Value passed as second argument of the callback.
397  *
398  * Iterate over all counters and call @cb for each one of them. Stop iterating if
399  * the callback returns FALSE.
400  *
401  */
402 void
403 mono_counters_foreach (CountersEnumCallback cb, gpointer user_data)
404 {
405         MonoCounter *counter;
406
407         if (!system_counters_initialized)
408                 initialize_system_counters ();
409
410         for (counter = counters; counter; counter = counter->next) {
411                 if (!cb (counter, user_data))
412                         return;
413         }
414 }
415
416 #define COPY_COUNTER(type,functype) do {        \
417                 size = sizeof (type);   \
418                 if (buffer_size < size) \
419                         return -1;      \
420                 *(type*)buffer = cb ? ((functype)counter->addr) () : *(type*)counter->addr; \
421         } while (0);
422
423 int
424 mono_counters_sample (MonoCounter *counter, void *buffer, int buffer_size)
425 {
426         int cb = counter->type & MONO_COUNTER_CALLBACK;
427         int size = -1;
428
429         char *strval;
430
431         switch (mono_counter_get_type (counter)) {
432         case MONO_COUNTER_INT:
433                 COPY_COUNTER (int, IntFunc);
434                 break;
435         case MONO_COUNTER_UINT:
436                 COPY_COUNTER (guint, UIntFunc);
437                 break;
438         case MONO_COUNTER_LONG:
439         case MONO_COUNTER_TIME_INTERVAL:
440                 COPY_COUNTER (gint64, LongFunc);
441                 break;
442         case MONO_COUNTER_ULONG:
443                 COPY_COUNTER (guint64, ULongFunc);
444                 break;
445         case MONO_COUNTER_WORD:
446                 COPY_COUNTER (gssize, PtrFunc);
447                 break;
448         case MONO_COUNTER_DOUBLE:
449                 COPY_COUNTER (double, DoubleFunc);
450                 break;
451         case MONO_COUNTER_STRING:
452                 if (buffer_size < counter->size)
453                         return -1;
454                 if (counter->size == 0)
455                         return 0;
456                 strval = cb ? ((StrFunc)counter->addr) () : (char*)counter->addr;
457                 if (!strval)
458                         return 0;
459                 size = counter->size;
460                 strncpy (buffer, strval, size - 1);
461                 ((char*)buffer)[size - 1] = '\0';
462         }
463
464         return size;
465 }
466
467 static const char
468 section_names [][10] = {
469         "JIT",
470         "GC",
471         "Metadata",
472         "Generics",
473         "Security",
474         "Runtime",
475         "System",
476 };
477
478 static void
479 mono_counters_dump_section (int section, int variance, FILE *outfile)
480 {
481         MonoCounter *counter = counters;
482         while (counter) {
483                 if ((counter->type & section) && (mono_counter_get_variance (counter) & variance))
484                         dump_counter (counter, outfile);
485                 counter = counter->next;
486         }
487 }
488
489 /**
490  * mono_counters_dump:
491  * @section_mask: The sections to dump counters for
492  * @outfile: a FILE to dump the results to
493  *
494  * Displays the counts of all the enabled counters registered. 
495  * To filter by variance, you can OR one or more variance with the specific section you want.
496  * Use MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance.
497  */
498 void
499 mono_counters_dump (int section_mask, FILE *outfile)
500 {
501         int i, j;
502         int variance;
503         section_mask &= valid_mask;
504         if (!counters)
505                 return;
506
507         variance = section_mask & MONO_COUNTER_VARIANCE_MASK;
508
509         /* If no variance mask is supplied, we default to all kinds. */
510         if (!variance)
511                 variance = MONO_COUNTER_VARIANCE_MASK;
512         section_mask &= ~MONO_COUNTER_VARIANCE_MASK;
513
514         for (j = 0, i = MONO_COUNTER_JIT; i < MONO_COUNTER_LAST_SECTION; j++, i <<= 1) {
515                 if ((section_mask & i) && (set_mask & i)) {
516                         fprintf (outfile, "\n%s statistics\n", section_names [j]);
517                         mono_counters_dump_section (i, variance, outfile);
518                 }
519         }
520
521         fflush (outfile);
522 }
523
524 /**
525  * mono_counters_cleanup:
526  *
527  * Perform any needed cleanup at process exit.
528  */
529 void
530 mono_counters_cleanup (void)
531 {
532         MonoCounter *counter = counters;
533         counters = NULL;
534         while (counter) {
535                 MonoCounter *tmp = counter;
536                 counter = counter->next;
537                 free (tmp);
538         }
539 }
540
541 static MonoResourceCallback limit_reached = NULL;
542 static uintptr_t resource_limits [MONO_RESOURCE_COUNT * 2];
543
544 /**
545  * mono_runtime_resource_check_limit:
546  * @resource_type: one of the #MonoResourceType enum values
547  * @value: the current value of the resource usage
548  *
549  * Check if a runtime resource limit has been reached. This function
550  * is intended to be used by the runtime only.
551  */
552 void
553 mono_runtime_resource_check_limit (int resource_type, uintptr_t value)
554 {
555         if (!limit_reached)
556                 return;
557         /* check the hard limit first */
558         if (value > resource_limits [resource_type * 2 + 1]) {
559                 limit_reached (resource_type, value, 0);
560                 return;
561         }
562         if (value > resource_limits [resource_type * 2])
563                 limit_reached (resource_type, value, 1);
564 }
565
566 /**
567  * mono_runtime_resource_limit:
568  * @resource_type: one of the #MonoResourceType enum values
569  * @soft_limit: the soft limit value
570  * @hard_limit: the hard limit value
571  *
572  * This function sets the soft and hard limit for runtime resources. When the limit
573  * is reached, a user-specified callback is called. The callback runs in a restricted
574  * environment, in which the world coult be stopped, so it can't take locks, perform
575  * allocations etc. The callback may be called multiple times once a limit has been reached
576  * if action is not taken to decrease the resource use.
577  *
578  * Returns: 0 on error or a positive integer otherwise.
579  */
580 int
581 mono_runtime_resource_limit (int resource_type, uintptr_t soft_limit, uintptr_t hard_limit)
582 {
583         if (resource_type >= MONO_RESOURCE_COUNT || resource_type < 0)
584                 return 0;
585         if (soft_limit > hard_limit)
586                 return 0;
587         resource_limits [resource_type * 2] = soft_limit;
588         resource_limits [resource_type * 2 + 1] = hard_limit;
589         return 1;
590 }
591
592 /**
593  * mono_runtime_resource_set_callback:
594  * @callback: a function pointer
595  * 
596  * Set the callback to be invoked when a resource limit is reached.
597  * The callback will receive the resource type, the resource amount in resource-specific
598  * units and a flag indicating whether the soft or hard limit was reached.
599  */
600 void
601 mono_runtime_resource_set_callback (MonoResourceCallback callback)
602 {
603         limit_reached = callback;
604 }
605
606