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