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