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