tracing: add per-event filtering
[linux-2.6.git] / kernel / trace / trace_events_filter.c
1 /*
2  * trace_events_filter - generic event filtering
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
19  */
20
21 #include <linux/debugfs.h>
22 #include <linux/uaccess.h>
23 #include <linux/module.h>
24 #include <linux/ctype.h>
25
26 #include "trace.h"
27
28 static int filter_pred_64(struct filter_pred *pred, void *event)
29 {
30         u64 *addr = (u64 *)(event + pred->offset);
31         u64 val = (u64)pred->val;
32         int match;
33
34         match = (val == *addr) ^ pred->not;
35
36         return match;
37 }
38
39 static int filter_pred_32(struct filter_pred *pred, void *event)
40 {
41         u32 *addr = (u32 *)(event + pred->offset);
42         u32 val = (u32)pred->val;
43         int match;
44
45         match = (val == *addr) ^ pred->not;
46
47         return match;
48 }
49
50 static int filter_pred_16(struct filter_pred *pred, void *event)
51 {
52         u16 *addr = (u16 *)(event + pred->offset);
53         u16 val = (u16)pred->val;
54         int match;
55
56         match = (val == *addr) ^ pred->not;
57
58         return match;
59 }
60
61 static int filter_pred_8(struct filter_pred *pred, void *event)
62 {
63         u8 *addr = (u8 *)(event + pred->offset);
64         u8 val = (u8)pred->val;
65         int match;
66
67         match = (val == *addr) ^ pred->not;
68
69         return match;
70 }
71
72 static int filter_pred_string(struct filter_pred *pred, void *event)
73 {
74         char *addr = (char *)(event + pred->offset);
75         int cmp, match;
76
77         cmp = strncmp(addr, pred->str_val, pred->str_len);
78
79         match = (!cmp) ^ pred->not;
80
81         return match;
82 }
83
84 /* return 1 if event matches, 0 otherwise (discard) */
85 int filter_match_preds(struct ftrace_event_call *call, void *rec)
86 {
87         int i, matched, and_failed = 0;
88         struct filter_pred *pred;
89
90         for (i = 0; i < MAX_FILTER_PRED; i++) {
91                 if (call->preds[i]) {
92                         pred = call->preds[i];
93                         if (and_failed && !pred->or)
94                                 continue;
95                         matched = pred->fn(pred, rec);
96                         if (!matched && !pred->or) {
97                                 and_failed = 1;
98                                 continue;
99                         } else if (matched && pred->or)
100                                 return 1;
101                 } else
102                         break;
103         }
104
105         if (and_failed)
106                 return 0;
107
108         return 1;
109 }
110
111 int filter_print_preds(struct filter_pred **preds, char *buf)
112 {
113         ssize_t this_len = 0;
114         char *field_name;
115         struct filter_pred *pred;
116         int i;
117
118         if (!preds) {
119                 this_len += sprintf(buf + this_len, "none\n");
120                 return this_len;
121         }
122
123         for (i = 0; i < MAX_FILTER_PRED; i++) {
124                 if (preds[i]) {
125                         pred = preds[i];
126                         field_name = pred->field_name;
127                         if (i)
128                                 this_len += sprintf(buf + this_len,
129                                             pred->or ? "|| " : "&& ");
130                         this_len += sprintf(buf + this_len,
131                                             "%s ", field_name);
132                         this_len += sprintf(buf + this_len,
133                                             pred->not ? "!= " : "== ");
134                         if (pred->str_val)
135                                 this_len += sprintf(buf + this_len,
136                                                     "%s\n", pred->str_val);
137                         else
138                                 this_len += sprintf(buf + this_len,
139                                                     "%llu\n", pred->val);
140                 } else
141                         break;
142         }
143
144         return this_len;
145 }
146
147 static struct ftrace_event_field *
148 find_event_field(struct ftrace_event_call *call, char *name)
149 {
150         struct ftrace_event_field *field;
151         struct list_head *entry, *tmp;
152
153         list_for_each_safe(entry, tmp, &call->fields) {
154                 field = list_entry(entry, struct ftrace_event_field, link);
155                 if (!strcmp(field->name, name))
156                         return field;
157         }
158
159         return NULL;
160 }
161
162 void filter_free_pred(struct filter_pred *pred)
163 {
164         if (!pred)
165                 return;
166
167         kfree(pred->field_name);
168         kfree(pred->str_val);
169         kfree(pred);
170 }
171
172 void filter_free_preds(struct ftrace_event_call *call)
173 {
174         int i;
175
176         if (call->preds) {
177                 for (i = 0; i < MAX_FILTER_PRED; i++)
178                         filter_free_pred(call->preds[i]);
179                 kfree(call->preds);
180                 call->preds = NULL;
181         }
182 }
183
184 static int __filter_add_pred(struct ftrace_event_call *call,
185                              struct filter_pred *pred)
186 {
187         int i;
188
189         if (call->preds && !pred->compound)
190                 filter_free_preds(call);
191
192         if (!call->preds) {
193                 call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred),
194                                       GFP_KERNEL);
195                 if (!call->preds)
196                         return -ENOMEM;
197         }
198
199         for (i = 0; i < MAX_FILTER_PRED; i++) {
200                 if (!call->preds[i]) {
201                         call->preds[i] = pred;
202                         return 0;
203                 }
204         }
205
206         return -ENOMEM;
207 }
208
209 static int is_string_field(const char *type)
210 {
211         if (strchr(type, '[') && strstr(type, "char"))
212                 return 1;
213
214         return 0;
215 }
216
217 int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred)
218 {
219         struct ftrace_event_field *field;
220
221         field = find_event_field(call, pred->field_name);
222         if (!field)
223                 return -EINVAL;
224
225         pred->offset = field->offset;
226
227         if (is_string_field(field->type)) {
228                 pred->fn = filter_pred_string;
229                 pred->str_len = field->size;
230                 return __filter_add_pred(call, pred);
231         }
232
233         switch (field->size) {
234         case 8:
235                 pred->fn = filter_pred_64;
236                 break;
237         case 4:
238                 pred->fn = filter_pred_32;
239                 break;
240         case 2:
241                 pred->fn = filter_pred_16;
242                 break;
243         case 1:
244                 pred->fn = filter_pred_8;
245                 break;
246         default:
247                 return -EINVAL;
248         }
249
250         return __filter_add_pred(call, pred);
251 }
252
253 int filter_parse(char **pbuf, struct filter_pred *pred)
254 {
255         char *tmp, *tok, *val_str = NULL;
256         int tok_n = 0;
257
258         /* field ==/!= number, or/and field ==/!= number, number */
259         while ((tok = strsep(pbuf, " \n"))) {
260                 if (tok_n == 0) {
261                         if (!strcmp(tok, "0")) {
262                                 pred->clear = 1;
263                                 return 0;
264                         } else if (!strcmp(tok, "&&")) {
265                                 pred->or = 0;
266                                 pred->compound = 1;
267                         } else if (!strcmp(tok, "||")) {
268                                 pred->or = 1;
269                                 pred->compound = 1;
270                         } else
271                                 pred->field_name = tok;
272                         tok_n = 1;
273                         continue;
274                 }
275                 if (tok_n == 1) {
276                         if (!pred->field_name)
277                                 pred->field_name = tok;
278                         else if (!strcmp(tok, "!="))
279                                 pred->not = 1;
280                         else if (!strcmp(tok, "=="))
281                                 pred->not = 0;
282                         else {
283                                 pred->field_name = NULL;
284                                 return -EINVAL;
285                         }
286                         tok_n = 2;
287                         continue;
288                 }
289                 if (tok_n == 2) {
290                         if (pred->compound) {
291                                 if (!strcmp(tok, "!="))
292                                         pred->not = 1;
293                                 else if (!strcmp(tok, "=="))
294                                         pred->not = 0;
295                                 else {
296                                         pred->field_name = NULL;
297                                         return -EINVAL;
298                                 }
299                         } else {
300                                 val_str = tok;
301                                 break; /* done */
302                         }
303                         tok_n = 3;
304                         continue;
305                 }
306                 if (tok_n == 3) {
307                         val_str = tok;
308                         break; /* done */
309                 }
310         }
311
312         pred->field_name = kstrdup(pred->field_name, GFP_KERNEL);
313         if (!pred->field_name)
314                 return -ENOMEM;
315
316         pred->val = simple_strtoull(val_str, &tmp, 10);
317         if (tmp == val_str) {
318                 pred->str_val = kstrdup(val_str, GFP_KERNEL);
319                 if (!pred->str_val)
320                         return -ENOMEM;
321         }
322
323         return 0;
324 }
325
326