]>
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" | |
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 | { | |
75c8b417 | 150 | struct ftrace_event_field *field, *next; |
7ce7e424 | 151 | |
75c8b417 | 152 | list_for_each_entry_safe(field, next, &call->fields, link) { |
7ce7e424 TZ |
153 | if (!strcmp(field->name, name)) |
154 | return field; | |
155 | } | |
156 | ||
157 | return NULL; | |
158 | } | |
159 | ||
160 | void filter_free_pred(struct filter_pred *pred) | |
161 | { | |
162 | if (!pred) | |
163 | return; | |
164 | ||
165 | kfree(pred->field_name); | |
166 | kfree(pred->str_val); | |
167 | kfree(pred); | |
168 | } | |
169 | ||
170 | void filter_free_preds(struct ftrace_event_call *call) | |
171 | { | |
172 | int i; | |
173 | ||
174 | if (call->preds) { | |
175 | for (i = 0; i < MAX_FILTER_PRED; i++) | |
176 | filter_free_pred(call->preds[i]); | |
177 | kfree(call->preds); | |
178 | call->preds = NULL; | |
179 | } | |
180 | } | |
181 | ||
cfb180f3 TZ |
182 | void filter_free_subsystem_preds(struct event_subsystem *system) |
183 | { | |
184 | struct ftrace_event_call *call = __start_ftrace_events; | |
185 | int i; | |
186 | ||
187 | if (system->preds) { | |
188 | for (i = 0; i < MAX_FILTER_PRED; i++) | |
189 | filter_free_pred(system->preds[i]); | |
190 | kfree(system->preds); | |
191 | system->preds = NULL; | |
192 | } | |
193 | ||
194 | events_for_each(call) { | |
195 | if (!call->name || !call->regfunc) | |
196 | continue; | |
197 | ||
198 | if (!strcmp(call->system, system->name)) | |
199 | filter_free_preds(call); | |
200 | } | |
201 | } | |
202 | ||
7ce7e424 TZ |
203 | static int __filter_add_pred(struct ftrace_event_call *call, |
204 | struct filter_pred *pred) | |
205 | { | |
206 | int i; | |
207 | ||
208 | if (call->preds && !pred->compound) | |
209 | filter_free_preds(call); | |
210 | ||
211 | if (!call->preds) { | |
212 | call->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), | |
213 | GFP_KERNEL); | |
214 | if (!call->preds) | |
215 | return -ENOMEM; | |
216 | } | |
217 | ||
218 | for (i = 0; i < MAX_FILTER_PRED; i++) { | |
219 | if (!call->preds[i]) { | |
220 | call->preds[i] = pred; | |
221 | return 0; | |
222 | } | |
223 | } | |
224 | ||
225 | return -ENOMEM; | |
226 | } | |
227 | ||
228 | static int is_string_field(const char *type) | |
229 | { | |
230 | if (strchr(type, '[') && strstr(type, "char")) | |
231 | return 1; | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | int filter_add_pred(struct ftrace_event_call *call, struct filter_pred *pred) | |
237 | { | |
238 | struct ftrace_event_field *field; | |
239 | ||
240 | field = find_event_field(call, pred->field_name); | |
241 | if (!field) | |
242 | return -EINVAL; | |
243 | ||
244 | pred->offset = field->offset; | |
245 | ||
246 | if (is_string_field(field->type)) { | |
247 | pred->fn = filter_pred_string; | |
248 | pred->str_len = field->size; | |
249 | return __filter_add_pred(call, pred); | |
250 | } | |
251 | ||
252 | switch (field->size) { | |
253 | case 8: | |
254 | pred->fn = filter_pred_64; | |
255 | break; | |
256 | case 4: | |
257 | pred->fn = filter_pred_32; | |
258 | break; | |
259 | case 2: | |
260 | pred->fn = filter_pred_16; | |
261 | break; | |
262 | case 1: | |
263 | pred->fn = filter_pred_8; | |
264 | break; | |
265 | default: | |
266 | return -EINVAL; | |
267 | } | |
268 | ||
269 | return __filter_add_pred(call, pred); | |
270 | } | |
271 | ||
cfb180f3 TZ |
272 | static struct filter_pred *copy_pred(struct filter_pred *pred) |
273 | { | |
274 | struct filter_pred *new_pred = kmalloc(sizeof(*pred), GFP_KERNEL); | |
275 | if (!new_pred) | |
276 | return NULL; | |
277 | ||
278 | memcpy(new_pred, pred, sizeof(*pred)); | |
ee6cdabc TZ |
279 | |
280 | if (pred->field_name) { | |
281 | new_pred->field_name = kstrdup(pred->field_name, GFP_KERNEL); | |
282 | if (!new_pred->field_name) { | |
283 | kfree(new_pred); | |
284 | return NULL; | |
285 | } | |
286 | } | |
287 | ||
cfb180f3 TZ |
288 | if (pred->str_val) { |
289 | new_pred->str_val = kstrdup(pred->str_val, GFP_KERNEL); | |
cfb180f3 | 290 | if (!new_pred->str_val) { |
ee6cdabc | 291 | filter_free_pred(new_pred); |
cfb180f3 TZ |
292 | return NULL; |
293 | } | |
294 | } | |
295 | ||
296 | return new_pred; | |
297 | } | |
298 | ||
299 | int filter_add_subsystem_pred(struct event_subsystem *system, | |
300 | struct filter_pred *pred) | |
301 | { | |
302 | struct ftrace_event_call *call = __start_ftrace_events; | |
303 | struct filter_pred *event_pred; | |
304 | int i; | |
305 | ||
306 | if (system->preds && !pred->compound) | |
307 | filter_free_subsystem_preds(system); | |
308 | ||
309 | if (!system->preds) { | |
310 | system->preds = kzalloc(MAX_FILTER_PRED * sizeof(pred), | |
311 | GFP_KERNEL); | |
312 | if (!system->preds) | |
313 | return -ENOMEM; | |
314 | } | |
315 | ||
316 | for (i = 0; i < MAX_FILTER_PRED; i++) { | |
317 | if (!system->preds[i]) { | |
318 | system->preds[i] = pred; | |
319 | break; | |
320 | } | |
cfb180f3 TZ |
321 | } |
322 | ||
c4cff064 TZ |
323 | if (i == MAX_FILTER_PRED) |
324 | return -EINVAL; | |
325 | ||
cfb180f3 | 326 | events_for_each(call) { |
c4cff064 TZ |
327 | int err; |
328 | ||
cfb180f3 TZ |
329 | if (!call->name || !call->regfunc) |
330 | continue; | |
331 | ||
c4cff064 TZ |
332 | if (strcmp(call->system, system->name)) |
333 | continue; | |
334 | ||
335 | if (!find_event_field(call, pred->field_name)) | |
336 | continue; | |
337 | ||
338 | event_pred = copy_pred(pred); | |
339 | if (!event_pred) | |
340 | goto oom; | |
341 | ||
342 | err = filter_add_pred(call, event_pred); | |
343 | if (err) | |
344 | filter_free_pred(event_pred); | |
345 | if (err == -ENOMEM) | |
346 | goto oom; | |
cfb180f3 TZ |
347 | } |
348 | ||
349 | return 0; | |
c4cff064 TZ |
350 | |
351 | oom: | |
352 | system->preds[i] = NULL; | |
353 | return -ENOMEM; | |
cfb180f3 TZ |
354 | } |
355 | ||
7ce7e424 TZ |
356 | int filter_parse(char **pbuf, struct filter_pred *pred) |
357 | { | |
358 | char *tmp, *tok, *val_str = NULL; | |
359 | int tok_n = 0; | |
360 | ||
361 | /* field ==/!= number, or/and field ==/!= number, number */ | |
362 | while ((tok = strsep(pbuf, " \n"))) { | |
363 | if (tok_n == 0) { | |
364 | if (!strcmp(tok, "0")) { | |
365 | pred->clear = 1; | |
366 | return 0; | |
367 | } else if (!strcmp(tok, "&&")) { | |
368 | pred->or = 0; | |
369 | pred->compound = 1; | |
370 | } else if (!strcmp(tok, "||")) { | |
371 | pred->or = 1; | |
372 | pred->compound = 1; | |
373 | } else | |
374 | pred->field_name = tok; | |
375 | tok_n = 1; | |
376 | continue; | |
377 | } | |
378 | if (tok_n == 1) { | |
379 | if (!pred->field_name) | |
380 | pred->field_name = tok; | |
381 | else if (!strcmp(tok, "!=")) | |
382 | pred->not = 1; | |
383 | else if (!strcmp(tok, "==")) | |
384 | pred->not = 0; | |
385 | else { | |
386 | pred->field_name = NULL; | |
387 | return -EINVAL; | |
388 | } | |
389 | tok_n = 2; | |
390 | continue; | |
391 | } | |
392 | if (tok_n == 2) { | |
393 | if (pred->compound) { | |
394 | if (!strcmp(tok, "!=")) | |
395 | pred->not = 1; | |
396 | else if (!strcmp(tok, "==")) | |
397 | pred->not = 0; | |
398 | else { | |
399 | pred->field_name = NULL; | |
400 | return -EINVAL; | |
401 | } | |
402 | } else { | |
403 | val_str = tok; | |
404 | break; /* done */ | |
405 | } | |
406 | tok_n = 3; | |
407 | continue; | |
408 | } | |
409 | if (tok_n == 3) { | |
410 | val_str = tok; | |
411 | break; /* done */ | |
412 | } | |
413 | } | |
414 | ||
415 | pred->field_name = kstrdup(pred->field_name, GFP_KERNEL); | |
416 | if (!pred->field_name) | |
417 | return -ENOMEM; | |
418 | ||
419 | pred->val = simple_strtoull(val_str, &tmp, 10); | |
420 | if (tmp == val_str) { | |
421 | pred->str_val = kstrdup(val_str, GFP_KERNEL); | |
422 | if (!pred->str_val) | |
423 | return -ENOMEM; | |
424 | } | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 |