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