Commit | Line | Data |
---|---|---|
7ce7e424 TZ |
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" | |
4bda2d51 | 27 | #include "trace_output.h" |
7ce7e424 TZ |
28 | |
29 | static int filter_pred_64(struct filter_pred *pred, void *event) | |
30 | { | |
31 | u64 *addr = (u64 *)(event + pred->offset); | |
32 | u64 val = (u64)pred->val; | |
33 | int match; | |
34 | ||
35 | match = (val == *addr) ^ pred->not; | |
36 | ||
37 | return match; | |
38 | } | |
39 | ||
40 | static int filter_pred_32(struct filter_pred *pred, void *event) | |
41 | { | |
42 | u32 *addr = (u32 *)(event + pred->offset); | |
43 | u32 val = (u32)pred->val; | |
44 | int match; | |
45 | ||
46 | match = (val == *addr) ^ pred->not; | |
47 | ||
48 | return match; | |
49 | } | |
50 | ||
51 | static int filter_pred_16(struct filter_pred *pred, void *event) | |
52 | { | |
53 | u16 *addr = (u16 *)(event + pred->offset); | |
54 | u16 val = (u16)pred->val; | |
55 | int match; | |
56 | ||
57 | match = (val == *addr) ^ pred->not; | |
58 | ||
59 | return match; | |
60 | } | |
61 | ||
62 | static int filter_pred_8(struct filter_pred *pred, void *event) | |
63 | { | |
64 | u8 *addr = (u8 *)(event + pred->offset); | |
65 | u8 val = (u8)pred->val; | |
66 | int match; | |
67 | ||
68 | match = (val == *addr) ^ pred->not; | |
69 | ||
70 | return match; | |
71 | } | |
72 | ||
73 | static int filter_pred_string(struct filter_pred *pred, void *event) | |
74 | { | |
75 | char *addr = (char *)(event + pred->offset); | |
76 | int cmp, match; | |
77 | ||
78 | cmp = strncmp(addr, pred->str_val, pred->str_len); | |
79 | ||
80 | match = (!cmp) ^ pred->not; | |
81 | ||
82 | return match; | |
83 | } | |
84 | ||
0a19e53c TZ |
85 | static int filter_pred_none(struct filter_pred *pred, void *event) |
86 | { | |
87 | return 0; | |
88 | } | |
89 | ||
7ce7e424 TZ |
90 | /* return 1 if event matches, 0 otherwise (discard) */ |
91 | int filter_match_preds(struct ftrace_event_call *call, void *rec) | |
92 | { | |
93 | int i, matched, and_failed = 0; | |
94 | struct filter_pred *pred; | |
95 | ||
0a19e53c TZ |
96 | for (i = 0; i < call->n_preds; i++) { |
97 | pred = call->preds[i]; | |
98 | if (and_failed && !pred->or) | |
99 | continue; | |
100 | matched = pred->fn(pred, rec); | |
101 | if (!matched && !pred->or) { | |
102 | and_failed = 1; | |
103 | continue; | |
104 | } else if (matched && pred->or) | |
105 | return 1; | |
7ce7e424 TZ |
106 | } |
107 | ||
108 | if (and_failed) | |
109 | return 0; | |
110 | ||
111 | return 1; | |
112 | } | |
17c873ec | 113 | EXPORT_SYMBOL_GPL(filter_match_preds); |
7ce7e424 | 114 | |
0a19e53c TZ |
115 | void filter_print_preds(struct filter_pred **preds, int n_preds, |
116 | struct trace_seq *s) | |
7ce7e424 | 117 | { |
7ce7e424 TZ |
118 | char *field_name; |
119 | struct filter_pred *pred; | |
120 | int i; | |
121 | ||
0a19e53c | 122 | if (!n_preds) { |
4bda2d51 TZ |
123 | trace_seq_printf(s, "none\n"); |
124 | return; | |
7ce7e424 TZ |
125 | } |
126 | ||
0a19e53c TZ |
127 | for (i = 0; i < n_preds; i++) { |
128 | pred = preds[i]; | |
129 | field_name = pred->field_name; | |
130 | if (i) | |
131 | trace_seq_printf(s, pred->or ? "|| " : "&& "); | |
132 | trace_seq_printf(s, "%s ", field_name); | |
133 | trace_seq_printf(s, pred->not ? "!= " : "== "); | |
134 | if (pred->str_len) | |
135 | trace_seq_printf(s, "%s\n", pred->str_val); | |
136 | else | |
137 | trace_seq_printf(s, "%llu\n", pred->val); | |
7ce7e424 | 138 | } |
7ce7e424 TZ |
139 | } |
140 | ||
141 | static struct ftrace_event_field * | |
142 | find_event_field(struct ftrace_event_call *call, char *name) | |
143 | { | |
1fc2d5c1 | 144 | struct ftrace_event_field *field; |
7ce7e424 | 145 | |
1fc2d5c1 | 146 | list_for_each_entry(field, &call->fields, link) { |
7ce7e424 TZ |
147 | if (!strcmp(field->name, name)) |
148 | return field; | |
149 | } | |
150 | ||
151 | return NULL; | |
152 | } | |
153 | ||
154 | void filter_free_pred(struct filter_pred *pred) | |
155 | { | |
156 | if (!pred) | |
157 | return; | |
158 | ||
159 | kfree(pred->field_name); | |
7ce7e424 TZ |
160 | kfree(pred); |
161 | } | |
162 | ||
0a19e53c TZ |
163 | static void filter_clear_pred(struct filter_pred *pred) |
164 | { | |
165 | kfree(pred->field_name); | |
166 | pred->field_name = NULL; | |
167 | pred->str_len = 0; | |
168 | } | |
169 | ||
170 | static int filter_set_pred(struct filter_pred *dest, | |
171 | struct filter_pred *src, | |
172 | filter_pred_fn_t fn) | |
173 | { | |
174 | *dest = *src; | |
175 | dest->field_name = kstrdup(src->field_name, GFP_KERNEL); | |
176 | if (!dest->field_name) | |
177 | return -ENOMEM; | |
178 | dest->fn = fn; | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | void filter_disable_preds(struct ftrace_event_call *call) | |
7ce7e424 TZ |
184 | { |
185 | int i; | |
186 | ||
0a19e53c TZ |
187 | call->n_preds = 0; |
188 | ||
189 | for (i = 0; i < MAX_FILTER_PRED; i++) | |
190 | call->preds[i]->fn = filter_pred_none; | |
191 | } | |
192 | ||
193 | int init_preds(struct ftrace_event_call *call) | |
194 | { | |
195 | struct filter_pred *pred; | |
196 | int i; | |
197 | ||
198 | call->n_preds = 0; | |
199 | ||
200 | call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), GFP_KERNEL); | |
201 | if (!call->preds) | |
202 | return -ENOMEM; | |
203 | ||
204 | for (i = 0; i < MAX_FILTER_PRED; i++) { | |
205 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | |
206 | if (!pred) | |
207 | goto oom; | |
208 | pred->fn = filter_pred_none; | |
209 | call->preds[i] = pred; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | ||
214 | oom: | |
215 | for (i = 0; i < MAX_FILTER_PRED; i++) { | |
216 | if (call->preds[i]) | |
7ce7e424 | 217 | filter_free_pred(call->preds[i]); |
7ce7e424 | 218 | } |
0a19e53c TZ |
219 | kfree(call->preds); |
220 | call->preds = NULL; | |
221 | ||
222 | return -ENOMEM; | |
7ce7e424 | 223 | } |
17c873ec | 224 | EXPORT_SYMBOL_GPL(init_preds); |
7ce7e424 | 225 | |
cfb180f3 TZ |
226 | void filter_free_subsystem_preds(struct event_subsystem *system) |
227 | { | |
a59fd602 | 228 | struct ftrace_event_call *call; |
cfb180f3 TZ |
229 | int i; |
230 | ||
0a19e53c TZ |
231 | if (system->n_preds) { |
232 | for (i = 0; i < system->n_preds; i++) | |
cfb180f3 TZ |
233 | filter_free_pred(system->preds[i]); |
234 | kfree(system->preds); | |
235 | system->preds = NULL; | |
0a19e53c | 236 | system->n_preds = 0; |
cfb180f3 TZ |
237 | } |
238 | ||
a59fd602 | 239 | list_for_each_entry(call, &ftrace_events, list) { |
e1112b4d | 240 | if (!call->define_fields) |
cfb180f3 TZ |
241 | continue; |
242 | ||
243 | if (!strcmp(call->system, system->name)) | |
0a19e53c | 244 | filter_disable_preds(call); |
cfb180f3 TZ |
245 | } |
246 | } | |
247 | ||
7ce7e424 | 248 | static int __filter_add_pred(struct ftrace_event_call *call, |
0a19e53c TZ |
249 | struct filter_pred *pred, |
250 | filter_pred_fn_t fn) | |
7ce7e424 | 251 | { |
0a19e53c | 252 | int idx, err; |
7ce7e424 | 253 | |
0a19e53c TZ |
254 | if (call->n_preds && !pred->compound) |
255 | filter_disable_preds(call); | |
7ce7e424 | 256 | |
0a19e53c TZ |
257 | if (call->n_preds == MAX_FILTER_PRED) |
258 | return -ENOSPC; | |
7ce7e424 | 259 | |
0a19e53c TZ |
260 | idx = call->n_preds; |
261 | filter_clear_pred(call->preds[idx]); | |
262 | err = filter_set_pred(call->preds[idx], pred, fn); | |
263 | if (err) | |
264 | return err; | |
265 | ||
266 | call->n_preds++; | |
7ce7e424 | 267 | |
0a19e53c | 268 | return 0; |
7ce7e424 TZ |
269 | } |
270 | ||
271 | static int is_string_field(const char *type) | |
272 | { | |
273 | if (strchr(type, '[') && strstr(type, "char")) | |
274 | return 1; | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | |
280 | { | |
281 | struct ftrace_event_field *field; | |
0a19e53c | 282 | filter_pred_fn_t fn; |
7ce7e424 TZ |
283 | |
284 | field = find_event_field(call, pred->field_name); | |
285 | if (!field) | |
286 | return -EINVAL; | |
287 | ||
0a19e53c | 288 | pred->fn = filter_pred_none; |
7ce7e424 TZ |
289 | pred->offset = field->offset; |
290 | ||
291 | if (is_string_field(field->type)) { | |
0a19e53c | 292 | if (!pred->str_len) |
9f58a159 | 293 | return -EINVAL; |
0a19e53c | 294 | fn = filter_pred_string; |
7ce7e424 | 295 | pred->str_len = field->size; |
0a19e53c | 296 | return __filter_add_pred(call, pred, fn); |
9f58a159 | 297 | } else { |
0a19e53c | 298 | if (pred->str_len) |
9f58a159 | 299 | return -EINVAL; |
7ce7e424 TZ |
300 | } |
301 | ||
302 | switch (field->size) { | |
303 | case 8: | |
0a19e53c | 304 | fn = filter_pred_64; |
7ce7e424 TZ |
305 | break; |
306 | case 4: | |
0a19e53c | 307 | fn = filter_pred_32; |
7ce7e424 TZ |
308 | break; |
309 | case 2: | |
0a19e53c | 310 | fn = filter_pred_16; |
7ce7e424 TZ |
311 | break; |
312 | case 1: | |
0a19e53c | 313 | fn = filter_pred_8; |
7ce7e424 TZ |
314 | break; |
315 | default: | |
316 | return -EINVAL; | |
317 | } | |
318 | ||
0a19e53c | 319 | return __filter_add_pred(call, pred, fn); |
cfb180f3 TZ |
320 | } |
321 | ||
322 | int filter_add_subsystem_pred(struct event_subsystem *system, | |
323 | struct filter_pred *pred) | |
324 | { | |
a59fd602 | 325 | struct ftrace_event_call *call; |
cfb180f3 | 326 | |
0a19e53c | 327 | if (system->n_preds && !pred->compound) |
cfb180f3 TZ |
328 | filter_free_subsystem_preds(system); |
329 | ||
0a19e53c | 330 | if (!system->n_preds) { |
cfb180f3 TZ |
331 | system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), |
332 | GFP_KERNEL); | |
333 | if (!system->preds) | |
334 | return -ENOMEM; | |
335 | } | |
336 | ||
0a19e53c | 337 | if (system->n_preds == MAX_FILTER_PRED) |
44e9c8b7 | 338 | return -ENOSPC; |
c4cff064 | 339 | |
0a19e53c TZ |
340 | system->preds[system->n_preds] = pred; |
341 | ||
a59fd602 | 342 | list_for_each_entry(call, &ftrace_events, list) { |
c4cff064 TZ |
343 | int err; |
344 | ||
e1112b4d | 345 | if (!call->define_fields) |
cfb180f3 TZ |
346 | continue; |
347 | ||
c4cff064 TZ |
348 | if (strcmp(call->system, system->name)) |
349 | continue; | |
350 | ||
351 | if (!find_event_field(call, pred->field_name)) | |
352 | continue; | |
353 | ||
0a19e53c TZ |
354 | err = filter_add_pred(call, pred); |
355 | if (err == -ENOMEM) { | |
356 | system->preds[system->n_preds] = NULL; | |
357 | return err; | |
358 | } | |
cfb180f3 TZ |
359 | } |
360 | ||
0a19e53c | 361 | system->n_preds++; |
c4cff064 | 362 | |
0a19e53c | 363 | return 0; |
cfb180f3 TZ |
364 | } |
365 | ||
7ce7e424 TZ |
366 | int filter_parse(char **pbuf, struct filter_pred *pred) |
367 | { | |
368 | char *tmp, *tok, *val_str = NULL; | |
369 | int tok_n = 0; | |
370 | ||
371 | /* field ==/!= number, or/and field ==/!= number, number */ | |
372 | while ((tok = strsep(pbuf, " \n"))) { | |
373 | if (tok_n == 0) { | |
374 | if (!strcmp(tok, "0")) { | |
375 | pred->clear = 1; | |
376 | return 0; | |
377 | } else if (!strcmp(tok, "&&")) { | |
378 | pred->or = 0; | |
379 | pred->compound = 1; | |
380 | } else if (!strcmp(tok, "||")) { | |
381 | pred->or = 1; | |
382 | pred->compound = 1; | |
383 | } else | |
384 | pred->field_name = tok; | |
385 | tok_n = 1; | |
386 | continue; | |
387 | } | |
388 | if (tok_n == 1) { | |
389 | if (!pred->field_name) | |
390 | pred->field_name = tok; | |
391 | else if (!strcmp(tok, "!=")) | |
392 | pred->not = 1; | |
393 | else if (!strcmp(tok, "==")) | |
394 | pred->not = 0; | |
395 | else { | |
396 | pred->field_name = NULL; | |
397 | return -EINVAL; | |
398 | } | |
399 | tok_n = 2; | |
400 | continue; | |
401 | } | |
402 | if (tok_n == 2) { | |
403 | if (pred->compound) { | |
404 | if (!strcmp(tok, "!=")) | |
405 | pred->not = 1; | |
406 | else if (!strcmp(tok, "==")) | |
407 | pred->not = 0; | |
408 | else { | |
409 | pred->field_name = NULL; | |
410 | return -EINVAL; | |
411 | } | |
412 | } else { | |
413 | val_str = tok; | |
414 | break; /* done */ | |
415 | } | |
416 | tok_n = 3; | |
417 | continue; | |
418 | } | |
419 | if (tok_n == 3) { | |
420 | val_str = tok; | |
421 | break; /* done */ | |
422 | } | |
423 | } | |
424 | ||
0a19e53c TZ |
425 | if (!val_str || !strlen(val_str) |
426 | || strlen(val_str) >= MAX_FILTER_STR_VAL) { | |
bcabd91c LZ |
427 | pred->field_name = NULL; |
428 | return -EINVAL; | |
429 | } | |
430 | ||
7ce7e424 TZ |
431 | pred->field_name = kstrdup(pred->field_name, GFP_KERNEL); |
432 | if (!pred->field_name) | |
433 | return -ENOMEM; | |
434 | ||
0a19e53c | 435 | pred->str_len = 0; |
a3e0ab05 | 436 | pred->val = simple_strtoull(val_str, &tmp, 0); |
7ce7e424 | 437 | if (tmp == val_str) { |
0a19e53c TZ |
438 | strncpy(pred->str_val, val_str, MAX_FILTER_STR_VAL); |
439 | pred->str_len = strlen(val_str); | |
440 | pred->str_val[pred->str_len] = '\0'; | |
a3e0ab05 LZ |
441 | } else if (*tmp != '\0') |
442 | return -EINVAL; | |
7ce7e424 TZ |
443 | |
444 | return 0; | |
445 | } | |
446 | ||
447 |