Merge pull request #920
[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 "mono-counters.h"
9
10 struct _MonoCounter {
11         MonoCounter *next;
12         const char *name;
13         void *addr;
14         int type;
15 };
16
17 static MonoCounter *counters = NULL;
18 static int valid_mask = 0;
19 static int set_mask = 0;
20
21 static int
22 mono_counter_get_variance (MonoCounter *counter)
23 {
24         return counter->type & MONO_COUNTER_VARIANCE_MASK;
25 }
26
27 static int
28 mono_counter_get_unit (MonoCounter *counter)
29 {
30         return counter->type & MONO_COUNTER_UNIT_MASK;
31 }
32
33 /**
34  * mono_counters_enable:
35  * @section_mask: a mask listing the sections that will be displayed
36  *
37  * This is used to track which counters will be displayed.
38  */
39 void
40 mono_counters_enable (int section_mask)
41 {
42         valid_mask = section_mask & MONO_COUNTER_SECTION_MASK;
43 }
44
45 /**
46  * mono_counters_register:
47  * @name: The name for this counters.
48  * @type: One of the possible MONO_COUNTER types, or MONO_COUNTER_CALLBACK for a function pointer.
49  * @addr: The address to register.
50  *
51  * Register addr as the address of a counter of type type.
52  * Note that @name must be a valid string at all times until
53  * mono_counters_dump () is called.
54  *
55  * It may be a function pointer if MONO_COUNTER_CALLBACK is specified:
56  * the function should return the value and take no arguments.
57  */
58 void 
59 mono_counters_register (const char* name, int type, void *addr)
60 {
61         MonoCounter *counter;
62         if (!(type & valid_mask))
63                 return;
64
65         if ((type & MONO_COUNTER_VARIANCE_MASK) == 0)
66                 type |= MONO_COUNTER_MONOTONIC;
67
68         counter = malloc (sizeof (MonoCounter));
69         if (!counter)
70                 return;
71         counter->name = name;
72         counter->type = type;
73         counter->addr = addr;
74         counter->next = NULL;
75
76         set_mask |= type;
77
78         /* Append */
79         if (counters) {
80                 MonoCounter *item = counters;
81                 while (item->next)
82                         item = item->next;
83                 item->next = counter;
84         } else {
85                 counters = counter;
86         }
87 }
88
89 typedef int (*IntFunc) (void);
90 typedef guint (*UIntFunc) (void);
91 typedef gint64 (*LongFunc) (void);
92 typedef guint64 (*ULongFunc) (void);
93 typedef gssize (*PtrFunc) (void);
94 typedef double (*DoubleFunc) (void);
95 typedef char* (*StrFunc) (void);
96
97 #define ENTRY_FMT "%-36s: "
98 static void
99 dump_counter (MonoCounter *counter, FILE *outfile) {
100         int intval;
101         guint uintval;
102         gint64 int64val;
103         guint64 uint64val;
104         gssize wordval;
105         double dval;
106         const char *str;
107         switch (counter->type & MONO_COUNTER_TYPE_MASK) {
108         case MONO_COUNTER_INT:
109               if (counter->type & MONO_COUNTER_CALLBACK)
110                       intval = ((IntFunc)counter->addr) ();
111               else
112                       intval = *(int*)counter->addr;
113               fprintf (outfile, ENTRY_FMT "%d\n", counter->name, intval);
114               break;
115         case MONO_COUNTER_UINT:
116               if (counter->type & MONO_COUNTER_CALLBACK)
117                       uintval = ((UIntFunc)counter->addr) ();
118               else
119                       uintval = *(guint*)counter->addr;
120               fprintf (outfile, ENTRY_FMT "%u\n", counter->name, uintval);
121               break;
122         case MONO_COUNTER_LONG:
123               if (counter->type & MONO_COUNTER_CALLBACK)
124                       int64val = ((LongFunc)counter->addr) ();
125               else
126                       int64val = *(gint64*)counter->addr;
127               if (mono_counter_get_unit (counter) == MONO_COUNTER_TIME)
128                       fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)int64val / 10000.0);
129               else
130                       fprintf (outfile, ENTRY_FMT "%lld\n", counter->name, (long long)int64val);
131               break;
132         case MONO_COUNTER_ULONG:
133               if (counter->type & MONO_COUNTER_CALLBACK)
134                       uint64val = ((ULongFunc)counter->addr) ();
135               else
136                       uint64val = *(guint64*)counter->addr;
137               fprintf (outfile, ENTRY_FMT "%llu\n", counter->name, (unsigned long long)uint64val);
138               break;
139         case MONO_COUNTER_WORD:
140               if (counter->type & MONO_COUNTER_CALLBACK)
141                       wordval = ((PtrFunc)counter->addr) ();
142               else
143                       wordval = *(gssize*)counter->addr;
144               fprintf (outfile, ENTRY_FMT "%zd\n", counter->name, (gint64)wordval);
145               break;
146         case MONO_COUNTER_DOUBLE:
147               if (counter->type & MONO_COUNTER_CALLBACK)
148                       dval = ((DoubleFunc)counter->addr) ();
149               else
150                       dval = *(double*)counter->addr;
151               fprintf (outfile, ENTRY_FMT "%.4f\n", counter->name, dval);
152               break;
153         case MONO_COUNTER_STRING:
154               if (counter->type & MONO_COUNTER_CALLBACK)
155                       str = ((StrFunc)counter->addr) ();
156               else
157                       str = *(char**)counter->addr;
158               fprintf (outfile, ENTRY_FMT "%s\n", counter->name, str);
159               break;
160         case MONO_COUNTER_TIME_INTERVAL:
161                 if (counter->type & MONO_COUNTER_CALLBACK)
162                         int64val = ((LongFunc)counter->addr) ();
163                 else
164                         int64val = *(gint64*)counter->addr;
165                 fprintf (outfile, ENTRY_FMT "%.2f ms\n", counter->name, (double)int64val / 1000.0);
166                 break;
167         }
168 }
169
170
171 /**
172  * mono_counters_foreach:
173  * @cb: The callback that will be called for each counter.
174  * @user_data: Value passed as second argument of the callback.
175  *
176  * Iterate over all counters and call @cb for each one of them. Stop iterating if
177  * the callback returns FALSE.
178  *
179  */
180 void
181 mono_counters_foreach (CountersEnumCallback cb, gpointer user_data)
182 {
183         MonoCounter *counter;
184
185         for (counter = counters; counter; counter = counter->next) {
186                 if (!cb (counter, user_data))
187                         return;
188         }
189 }
190
191 static const char
192 section_names [][10] = {
193         "JIT",
194         "GC",
195         "Metadata",
196         "Generics",
197         "Security",
198         "Runtime",
199         "System",
200 };
201
202 static void
203 mono_counters_dump_section (int section, FILE *outfile)
204 {
205         MonoCounter *counter = counters;
206         while (counter) {
207                 if ((counter->type & section) && (mono_counter_get_variance (counter) & section))
208                         dump_counter (counter, outfile);
209                 counter = counter->next;
210         }
211 }
212
213 /**
214  * mono_counters_dump:
215  * @section_mask: The sections to dump counters for
216  * @outfile: a FILE to dump the results to
217  *
218  * Displays the counts of all the enabled counters registered. 
219  * To filter by variance, you can OR one or more variance with the specific section you want.
220  * Use MONO_COUNTER_SECTION_MASK to dump all categories of a specific variance.
221  */
222 void
223 mono_counters_dump (int section_mask, FILE *outfile)
224 {
225         int i, j;
226         section_mask &= valid_mask;
227         if (!counters)
228                 return;
229
230         /* If no variance mask is supplied, we default to all kinds. */
231         if (!(section_mask & MONO_COUNTER_VARIANCE_MASK))
232                 section_mask |= MONO_COUNTER_VARIANCE_MASK;
233
234         for (j = 0, i = MONO_COUNTER_JIT; i < MONO_COUNTER_LAST_SECTION; j++, i <<= 1) {
235                 if ((section_mask & i) && (set_mask & i)) {
236                         fprintf (outfile, "\n%s statistics\n", section_names [j]);
237                         mono_counters_dump_section (i | (section_mask & MONO_COUNTER_VARIANCE_MASK), outfile);
238                 }
239         }
240
241         fflush (outfile);
242 }
243
244 /**
245  * mono_counters_cleanup:
246  *
247  * Perform any needed cleanup at process exit.
248  */
249 void
250 mono_counters_cleanup (void)
251 {
252         MonoCounter *counter = counters;
253         counters = NULL;
254         while (counter) {
255                 MonoCounter *tmp = counter;
256                 counter = counter->next;
257                 free (tmp);
258         }
259 }
260
261 static MonoResourceCallback limit_reached = NULL;
262 static uintptr_t resource_limits [MONO_RESOURCE_COUNT * 2];
263
264 /**
265  * mono_runtime_resource_check_limit:
266  * @resource_type: one of the #MonoResourceType enum values
267  * @value: the current value of the resource usage
268  *
269  * Check if a runtime resource limit has been reached. This function
270  * is intended to be used by the runtime only.
271  */
272 void
273 mono_runtime_resource_check_limit (int resource_type, uintptr_t value)
274 {
275         if (!limit_reached)
276                 return;
277         /* check the hard limit first */
278         if (value > resource_limits [resource_type * 2 + 1]) {
279                 limit_reached (resource_type, value, 0);
280                 return;
281         }
282         if (value > resource_limits [resource_type * 2])
283                 limit_reached (resource_type, value, 1);
284 }
285
286 /**
287  * mono_runtime_resource_limit:
288  * @resource_type: one of the #MonoResourceType enum values
289  * @soft_limit: the soft limit value
290  * @hard_limit: the hard limit value
291  *
292  * This function sets the soft and hard limit for runtime resources. When the limit
293  * is reached, a user-specified callback is called. The callback runs in a restricted
294  * environment, in which the world coult be stopped, so it can't take locks, perform
295  * allocations etc. The callback may be called multiple times once a limit has been reached
296  * if action is not taken to decrease the resource use.
297  *
298  * Returns: 0 on error or a positive integer otherwise.
299  */
300 int
301 mono_runtime_resource_limit (int resource_type, uintptr_t soft_limit, uintptr_t hard_limit)
302 {
303         if (resource_type >= MONO_RESOURCE_COUNT || resource_type < 0)
304                 return 0;
305         if (soft_limit > hard_limit)
306                 return 0;
307         resource_limits [resource_type * 2] = soft_limit;
308         resource_limits [resource_type * 2 + 1] = hard_limit;
309         return 1;
310 }
311
312 /**
313  * mono_runtime_resource_set_callback:
314  * @callback: a function pointer
315  * 
316  * Set the callback to be invoked when a resource limit is reached.
317  * The callback will receive the resource type, the resource amount in resource-specific
318  * units and a flag indicating whether the soft or hard limit was reached.
319  */
320 void
321 mono_runtime_resource_set_callback (MonoResourceCallback callback)
322 {
323         limit_reached = callback;
324 }
325
326