[profiler] log profiler: limit method instrumentation to selected methods (#5517)
[mono.git] / mono / metadata / callspec.c
1 /**
2  * \file
3  * Call specification facilities for the Mono Runtime.
4  *
5  * Author:
6  *   Paolo Molaro (lupus@ximian.com)
7  *   Dietmar Maurer (dietmar@ximian.com)
8  *
9  * (C) 2002 Ximian, Inc.
10  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11  * Licensed under the MIT license. See LICENSE file in the project root for full
12  * license information.
13  */
14 #include "metadata.h"
15 #include "callspec.h"
16 #include "assembly.h"
17 #include "class-internals.h"
18 #include "debug-helpers.h"
19
20 static MonoAssembly *prog_assembly;
21
22 gboolean
23 mono_callspec_eval_exception (MonoClass *klass, MonoCallSpec *spec)
24 {
25         int include = 0;
26         int i;
27
28         if (!klass)
29                 return FALSE;
30
31         for (i = 0; i < spec->len; i++) {
32                 MonoTraceOperation *op = &spec->ops [i];
33                 int inc = 0;
34
35                 switch (op->op) {
36                 case MONO_TRACEOP_EXCEPTION:
37                         if (strcmp ("", op->data) == 0 &&
38                             strcmp ("all", op->data2) == 0)
39                                 inc = 1;
40                         else if (strcmp ("", op->data) == 0 ||
41                                  strcmp (klass->name_space, op->data) == 0)
42                                 if (strcmp (klass->name, op->data2) == 0)
43                                         inc = 1;
44                         break;
45                 default:
46                         break;
47                 }
48                 if (op->exclude) {
49                         if (inc)
50                                 include = 0;
51                 } else if (inc)
52                         include = 1;
53         }
54
55         return include;
56 }
57
58 gboolean mono_callspec_eval (MonoMethod *method, const MonoCallSpec *spec)
59 {
60         int include = 0;
61         int i;
62
63         for (i = 0; i < spec->len; i++) {
64                 MonoTraceOperation *op = &spec->ops[i];
65                 int inc = 0;
66
67                 switch (op->op) {
68                 case MONO_TRACEOP_ALL:
69                         inc = 1;
70                         break;
71                 case MONO_TRACEOP_PROGRAM:
72                         if (prog_assembly &&
73                             (method->klass->image ==
74                              mono_assembly_get_image (prog_assembly)))
75                                 inc = 1;
76                         break;
77                 case MONO_TRACEOP_WRAPPER:
78                         if ((method->wrapper_type ==
79                              MONO_WRAPPER_NATIVE_TO_MANAGED) ||
80                             (method->wrapper_type ==
81                              MONO_WRAPPER_MANAGED_TO_NATIVE))
82                                 inc = 1;
83                         break;
84                 case MONO_TRACEOP_METHOD:
85                         if (mono_method_desc_full_match (
86                                 (MonoMethodDesc *)op->data, method))
87                                 inc = 1;
88                         break;
89                 case MONO_TRACEOP_CLASS:
90                         if (strcmp (method->klass->name_space, op->data) == 0)
91                                 if (strcmp (method->klass->name, op->data2) ==
92                                     0)
93                                         inc = 1;
94                         break;
95                 case MONO_TRACEOP_ASSEMBLY:
96                         if (strcmp (mono_image_get_name (method->klass->image),
97                                     op->data) == 0)
98                                 inc = 1;
99                         break;
100                 case MONO_TRACEOP_NAMESPACE:
101                         if (strcmp (method->klass->name_space, op->data) == 0)
102                                 inc = 1;
103                         break;
104                 case MONO_TRACEOP_EXCEPTION:
105                         break;
106                 }
107                 if (op->exclude) {
108                         if (inc)
109                                 include = 0;
110                 } else if (inc) {
111                         include = 1;
112                 }
113         }
114         return include;
115 }
116
117 static int is_filenamechar (char p)
118 {
119         if (p >= 'A' && p <= 'Z')
120                 return TRUE;
121         if (p >= 'a' && p <= 'z')
122                 return TRUE;
123         if (p >= '0' && p <= '9')
124                 return TRUE;
125         if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
126                 return TRUE;
127         return FALSE;
128 }
129
130 static char *get_string (char **in)
131 {
132         char *start = *in;
133         char *p = *in;
134         while (is_filenamechar (*p)) {
135                 p++;
136         }
137         size_t len = p - start;
138         char *ret = (char *)g_malloc (len + 1);
139         memcpy (ret, start, len);
140         ret [len] = 0;
141         *in = p;
142         return ret;
143 }
144
145 enum Token {
146         TOKEN_METHOD,
147         TOKEN_CLASS,
148         TOKEN_ALL,
149         TOKEN_PROGRAM,
150         TOKEN_EXCEPTION,
151         TOKEN_NAMESPACE,
152         TOKEN_WRAPPER,
153         TOKEN_STRING,
154         TOKEN_EXCLUDE,
155         TOKEN_DISABLED,
156         TOKEN_SEPARATOR,
157         TOKEN_END,
158         TOKEN_ERROR
159 };
160
161 static int get_token (char **in, char **extra, char **errstr)
162 {
163         char *p = *in;
164         while (p[0] == '+')
165                 p++;
166
167         *extra = NULL;
168
169         if (p[0] == '\0') {
170                 *in = p;
171                 return TOKEN_END;
172         }
173         if (p[0] == 'M' && p[1] == ':') {
174                 p += 2;
175                 *extra = get_string (&p);
176                 *in = p;
177                 return TOKEN_METHOD;
178         }
179         if (p[0] == 'N' && p[1] == ':') {
180                 p += 2;
181                 *extra = get_string (&p);
182                 *in = p;
183                 return TOKEN_NAMESPACE;
184         }
185         if (p[0] == 'T' && p[1] == ':') {
186                 p += 2;
187                 *extra = get_string (&p);
188                 *in = p;
189                 return TOKEN_CLASS;
190         }
191         if (p[0] == 'E' && p[1] == ':') {
192                 p += 2;
193                 *extra = get_string (&p);
194                 *in = p;
195                 return TOKEN_EXCEPTION;
196         }
197         if (*p == '-') {
198                 p++;
199                 *in = p;
200                 return TOKEN_EXCLUDE;
201         }
202         if (is_filenamechar (*p)) {
203                 *extra = get_string (&p);
204                 *in = p;
205                 if (strcmp (*extra, "all") == 0)
206                         return TOKEN_ALL;
207                 if (strcmp (*extra, "program") == 0)
208                         return TOKEN_PROGRAM;
209                 if (strcmp (*extra, "wrapper") == 0)
210                         return TOKEN_WRAPPER;
211                 if (strcmp (*extra, "disabled") == 0)
212                         return TOKEN_DISABLED;
213                 return TOKEN_STRING;
214         }
215         if (*p == ',') {
216                 p++;
217                 *in = p;
218                 return TOKEN_SEPARATOR;
219         }
220
221         *errstr = g_strdup_printf ("Syntax error at or around '%s'", p);
222         return TOKEN_ERROR;
223 }
224
225 static int get_spec (char **in, MonoCallSpec *spec, char **errstr)
226 {
227         int n = spec->len;
228         char *extra = NULL;
229
230         int token = get_token (in, &extra, errstr);
231         gboolean exclude = FALSE;
232         if (token == TOKEN_EXCLUDE) {
233                 exclude = TRUE;
234                 token = get_token (in, &extra, errstr);
235                 if (token == TOKEN_EXCLUDE || token == TOKEN_DISABLED) {
236                         *errstr = g_strdup_printf ("Expecting an expression");
237                         token = TOKEN_ERROR;
238                         goto out;
239                 }
240         }
241         if (token == TOKEN_END || token == TOKEN_SEPARATOR ||
242             token == TOKEN_ERROR)
243                 goto out;
244
245         if (token == TOKEN_DISABLED) {
246                 spec->enabled = FALSE;
247                 goto out;
248         }
249
250         if (token == TOKEN_METHOD) {
251                 MonoMethodDesc *desc = mono_method_desc_new (extra, TRUE);
252                 if (desc == NULL) {
253                         *errstr =
254                             g_strdup_printf ("Invalid method name: %s", extra);
255                         token = TOKEN_ERROR;
256                         goto out;
257                 }
258                 spec->ops[n].op = MONO_TRACEOP_METHOD;
259                 spec->ops[n].data = desc;
260         } else if (token == TOKEN_ALL)
261                 spec->ops[n].op = MONO_TRACEOP_ALL;
262         else if (token == TOKEN_PROGRAM)
263                 spec->ops[n].op = MONO_TRACEOP_PROGRAM;
264         else if (token == TOKEN_WRAPPER)
265                 spec->ops[n].op = MONO_TRACEOP_WRAPPER;
266         else if (token == TOKEN_NAMESPACE) {
267                 spec->ops[n].op = MONO_TRACEOP_NAMESPACE;
268                 spec->ops[n].data = g_strdup (extra);
269         } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION) {
270                 char *p = strrchr (extra, '.');
271                 if (p) {
272                         *p++ = 0;
273                         spec->ops[n].data = g_strdup (extra);
274                         spec->ops[n].data2 = g_strdup (p);
275                 } else {
276                         spec->ops[n].data = g_strdup ("");
277                         spec->ops[n].data2 = g_strdup (extra);
278                 }
279                 spec->ops[n].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS
280                                                        : MONO_TRACEOP_EXCEPTION;
281         } else if (token == TOKEN_STRING) {
282                 spec->ops[n].op = MONO_TRACEOP_ASSEMBLY;
283                 spec->ops[n].data = g_strdup (extra);
284         } else {
285                 *errstr =
286                     g_strdup_printf ("Syntax error in method specification");
287                 token = TOKEN_ERROR;
288                 goto out;
289         }
290
291         if (exclude)
292                 spec->ops[n].exclude = 1;
293
294         spec->len = n + 1;
295         token = TOKEN_SEPARATOR;
296 out:
297         if (extra != NULL) {
298                 g_free (extra);
299         }
300         return token;
301 }
302
303 gboolean
304 mono_callspec_parse (const char *options, MonoCallSpec *spec, char **errstr)
305 {
306         char *p = (char *)options;
307         int size = 1;
308         int token;
309
310         memset (spec, 0, sizeof (*spec));
311         *errstr = NULL;
312
313         spec->enabled = TRUE;
314         if (*p == 0) {
315                 spec->len = 1;
316                 spec->ops = g_new0 (MonoTraceOperation, 1);
317                 spec->ops[0].op = MONO_TRACEOP_ALL;
318                 return TRUE;
319         }
320
321         for (p = (char *)options; *p != 0; p++)
322                 if (*p == ',')
323                         size++;
324
325         spec->ops = g_new0 (MonoTraceOperation, size);
326
327         p = (char *)options;
328
329         while ((token = (get_spec (&p, spec, errstr))) != TOKEN_END) {
330                 if (token == TOKEN_ERROR)
331                         return FALSE;
332         }
333         return TRUE;
334 }
335
336 void mono_callspec_cleanup (MonoCallSpec *spec)
337 {
338         if (spec->ops != NULL) {
339                 g_free (spec->ops);
340         }
341         memset (spec, 0, sizeof (*spec));
342 }
343
344 void
345 mono_callspec_set_assembly (MonoAssembly *assembly)
346 {
347         prog_assembly = assembly;
348 }