diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-09-24 15:10:44 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-09-24 15:39:27 -0400 |
commit | 1889d20922d14a97b2099fa4d47587217c0ba48b (patch) | |
tree | 5980f8473f3482aff577ad262e00d06d0745dcf1 | |
parent | f3f3f0092477d0165f3f1bf0fd518550b2abd097 (diff) |
tracing/filters: Provide basic regex support
This patch provides basic support for regular expressions in filters.
It supports the following types of regexp:
- *match_beginning
- *match_middle*
- match_end*
- !don't match
Example:
cd /debug/tracing/events/bkl/lock_kernel
echo 'file == "*reiserfs*"' > filter
echo 1 > enable
gedit-4941 [000] 457.735437: lock_kernel: depth: 0, fs/reiserfs/namei.c:334 reiserfs_lookup()
sync_supers-227 [001] 461.379985: lock_kernel: depth: 0, fs/reiserfs/super.c:69 reiserfs_sync_fs()
sync_supers-227 [000] 461.383096: lock_kernel: depth: 0, fs/reiserfs/journal.c:1069 flush_commit_list()
reiserfs/1-1369 [001] 461.479885: lock_kernel: depth: 0, fs/reiserfs/journal.c:3509 flush_async_commits()
Every string is now handled as a regexp in the filter framework, which
helps to factorize the code for handling both simple strings and
regexp comparisons.
(The regexp parsing code has been wildly cherry picked from ftrace.c
written by Steve.)
v2: Simplify the whole and drop the filter_regex file
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
-rw-r--r-- | kernel/trace/trace.h | 27 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 155 |
2 files changed, 157 insertions, 25 deletions
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 86bcff94791a..8d0db6018fe4 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -702,20 +702,29 @@ struct event_subsystem { | |||
702 | }; | 702 | }; |
703 | 703 | ||
704 | struct filter_pred; | 704 | struct filter_pred; |
705 | struct regex; | ||
705 | 706 | ||
706 | typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event, | 707 | typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event, |
707 | int val1, int val2); | 708 | int val1, int val2); |
708 | 709 | ||
710 | typedef int (*regex_match_func)(char *str, struct regex *r, int len); | ||
711 | |||
712 | struct regex { | ||
713 | char pattern[MAX_FILTER_STR_VAL]; | ||
714 | int len; | ||
715 | int field_len; | ||
716 | regex_match_func match; | ||
717 | }; | ||
718 | |||
709 | struct filter_pred { | 719 | struct filter_pred { |
710 | filter_pred_fn_t fn; | 720 | filter_pred_fn_t fn; |
711 | u64 val; | 721 | u64 val; |
712 | char str_val[MAX_FILTER_STR_VAL]; | 722 | struct regex regex; |
713 | int str_len; | 723 | char *field_name; |
714 | char *field_name; | 724 | int offset; |
715 | int offset; | 725 | int not; |
716 | int not; | 726 | int op; |
717 | int op; | 727 | int pop_n; |
718 | int pop_n; | ||
719 | }; | 728 | }; |
720 | 729 | ||
721 | extern void print_event_filter(struct ftrace_event_call *call, | 730 | extern void print_event_filter(struct ftrace_event_call *call, |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 189663d82aa7..d3c94c139567 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -195,9 +195,9 @@ static int filter_pred_string(struct filter_pred *pred, void *event, | |||
195 | char *addr = (char *)(event + pred->offset); | 195 | char *addr = (char *)(event + pred->offset); |
196 | int cmp, match; | 196 | int cmp, match; |
197 | 197 | ||
198 | cmp = strncmp(addr, pred->str_val, pred->str_len); | 198 | cmp = pred->regex.match(addr, &pred->regex, pred->regex.field_len); |
199 | 199 | ||
200 | match = (!cmp) ^ pred->not; | 200 | match = cmp ^ pred->not; |
201 | 201 | ||
202 | return match; | 202 | return match; |
203 | } | 203 | } |
@@ -209,9 +209,9 @@ static int filter_pred_pchar(struct filter_pred *pred, void *event, | |||
209 | char **addr = (char **)(event + pred->offset); | 209 | char **addr = (char **)(event + pred->offset); |
210 | int cmp, match; | 210 | int cmp, match; |
211 | 211 | ||
212 | cmp = strncmp(*addr, pred->str_val, pred->str_len); | 212 | cmp = pred->regex.match(*addr, &pred->regex, pred->regex.field_len); |
213 | 213 | ||
214 | match = (!cmp) ^ pred->not; | 214 | match = cmp ^ pred->not; |
215 | 215 | ||
216 | return match; | 216 | return match; |
217 | } | 217 | } |
@@ -235,9 +235,9 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event, | |||
235 | char *addr = (char *)(event + str_loc); | 235 | char *addr = (char *)(event + str_loc); |
236 | int cmp, match; | 236 | int cmp, match; |
237 | 237 | ||
238 | cmp = strncmp(addr, pred->str_val, str_len); | 238 | cmp = pred->regex.match(addr, &pred->regex, str_len); |
239 | 239 | ||
240 | match = (!cmp) ^ pred->not; | 240 | match = cmp ^ pred->not; |
241 | 241 | ||
242 | return match; | 242 | return match; |
243 | } | 243 | } |
@@ -248,6 +248,126 @@ static int filter_pred_none(struct filter_pred *pred, void *event, | |||
248 | return 0; | 248 | return 0; |
249 | } | 249 | } |
250 | 250 | ||
251 | /* Basic regex callbacks */ | ||
252 | static int regex_match_full(char *str, struct regex *r, int len) | ||
253 | { | ||
254 | if (strncmp(str, r->pattern, len) == 0) | ||
255 | return 1; | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int regex_match_front(char *str, struct regex *r, int len) | ||
260 | { | ||
261 | if (strncmp(str, r->pattern, len) == 0) | ||
262 | return 1; | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int regex_match_middle(char *str, struct regex *r, int len) | ||
267 | { | ||
268 | if (strstr(str, r->pattern)) | ||
269 | return 1; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int regex_match_end(char *str, struct regex *r, int len) | ||
274 | { | ||
275 | char *ptr = strstr(str, r->pattern); | ||
276 | |||
277 | if (ptr && (ptr[r->len] == 0)) | ||
278 | return 1; | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | enum regex_type { | ||
283 | MATCH_FULL, | ||
284 | MATCH_FRONT_ONLY, | ||
285 | MATCH_MIDDLE_ONLY, | ||
286 | MATCH_END_ONLY, | ||
287 | }; | ||
288 | |||
289 | /* | ||
290 | * Pass in a buffer containing a regex and this function will | ||
291 | * set search to point to the search part of the buffer and | ||
292 | * return the type of search it is (see enum above). | ||
293 | * This does modify buff. | ||
294 | * | ||
295 | * Returns enum type. | ||
296 | * search returns the pointer to use for comparison. | ||
297 | * not returns 1 if buff started with a '!' | ||
298 | * 0 otherwise. | ||
299 | */ | ||
300 | static enum regex_type | ||
301 | filter_parse_regex(char *buff, int len, char **search, int *not) | ||
302 | { | ||
303 | int type = MATCH_FULL; | ||
304 | int i; | ||
305 | |||
306 | if (buff[0] == '!') { | ||
307 | *not = 1; | ||
308 | buff++; | ||
309 | len--; | ||
310 | } else | ||
311 | *not = 0; | ||
312 | |||
313 | *search = buff; | ||
314 | |||
315 | for (i = 0; i < len; i++) { | ||
316 | if (buff[i] == '*') { | ||
317 | if (!i) { | ||
318 | *search = buff + 1; | ||
319 | type = MATCH_END_ONLY; | ||
320 | } else { | ||
321 | if (type == MATCH_END_ONLY) | ||
322 | type = MATCH_MIDDLE_ONLY; | ||
323 | else | ||
324 | type = MATCH_FRONT_ONLY; | ||
325 | buff[i] = 0; | ||
326 | break; | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return type; | ||
332 | } | ||
333 | |||
334 | static int filter_build_regex(struct filter_pred *pred) | ||
335 | { | ||
336 | struct regex *r = &pred->regex; | ||
337 | char *search, *dup; | ||
338 | enum regex_type type; | ||
339 | int not; | ||
340 | |||
341 | type = filter_parse_regex(r->pattern, r->len, &search, ¬); | ||
342 | dup = kstrdup(search, GFP_KERNEL); | ||
343 | if (!dup) | ||
344 | return -ENOMEM; | ||
345 | |||
346 | strcpy(r->pattern, dup); | ||
347 | kfree(dup); | ||
348 | |||
349 | r->len = strlen(r->pattern); | ||
350 | |||
351 | switch (type) { | ||
352 | case MATCH_FULL: | ||
353 | r->match = regex_match_full; | ||
354 | break; | ||
355 | case MATCH_FRONT_ONLY: | ||
356 | r->match = regex_match_front; | ||
357 | break; | ||
358 | case MATCH_MIDDLE_ONLY: | ||
359 | r->match = regex_match_middle; | ||
360 | break; | ||
361 | case MATCH_END_ONLY: | ||
362 | r->match = regex_match_end; | ||
363 | break; | ||
364 | } | ||
365 | |||
366 | pred->not ^= not; | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
251 | /* return 1 if event matches, 0 otherwise (discard) */ | 371 | /* return 1 if event matches, 0 otherwise (discard) */ |
252 | int filter_match_preds(struct ftrace_event_call *call, void *rec) | 372 | int filter_match_preds(struct ftrace_event_call *call, void *rec) |
253 | { | 373 | { |
@@ -394,7 +514,7 @@ static void filter_clear_pred(struct filter_pred *pred) | |||
394 | { | 514 | { |
395 | kfree(pred->field_name); | 515 | kfree(pred->field_name); |
396 | pred->field_name = NULL; | 516 | pred->field_name = NULL; |
397 | pred->str_len = 0; | 517 | pred->regex.len = 0; |
398 | } | 518 | } |
399 | 519 | ||
400 | static int filter_set_pred(struct filter_pred *dest, | 520 | static int filter_set_pred(struct filter_pred *dest, |
@@ -658,21 +778,24 @@ static int filter_add_pred(struct filter_parse_state *ps, | |||
658 | } | 778 | } |
659 | 779 | ||
660 | if (is_string_field(field)) { | 780 | if (is_string_field(field)) { |
661 | pred->str_len = field->size; | 781 | ret = filter_build_regex(pred); |
782 | if (ret) | ||
783 | return ret; | ||
662 | 784 | ||
663 | if (field->filter_type == FILTER_STATIC_STRING) | 785 | if (field->filter_type == FILTER_STATIC_STRING) { |
664 | fn = filter_pred_string; | 786 | fn = filter_pred_string; |
665 | else if (field->filter_type == FILTER_DYN_STRING) | 787 | pred->regex.field_len = field->size; |
666 | fn = filter_pred_strloc; | 788 | } else if (field->filter_type == FILTER_DYN_STRING) |
789 | fn = filter_pred_strloc; | ||
667 | else { | 790 | else { |
668 | fn = filter_pred_pchar; | 791 | fn = filter_pred_pchar; |
669 | pred->str_len = strlen(pred->str_val); | 792 | pred->regex.field_len = strlen(pred->regex.pattern); |
670 | } | 793 | } |
671 | } else { | 794 | } else { |
672 | if (field->is_signed) | 795 | if (field->is_signed) |
673 | ret = strict_strtoll(pred->str_val, 0, &val); | 796 | ret = strict_strtoll(pred->regex.pattern, 0, &val); |
674 | else | 797 | else |
675 | ret = strict_strtoull(pred->str_val, 0, &val); | 798 | ret = strict_strtoull(pred->regex.pattern, 0, &val); |
676 | if (ret) { | 799 | if (ret) { |
677 | parse_error(ps, FILT_ERR_ILLEGAL_INTVAL, 0); | 800 | parse_error(ps, FILT_ERR_ILLEGAL_INTVAL, 0); |
678 | return -EINVAL; | 801 | return -EINVAL; |
@@ -1042,8 +1165,8 @@ static struct filter_pred *create_pred(int op, char *operand1, char *operand2) | |||
1042 | return NULL; | 1165 | return NULL; |
1043 | } | 1166 | } |
1044 | 1167 | ||
1045 | strcpy(pred->str_val, operand2); | 1168 | strcpy(pred->regex.pattern, operand2); |
1046 | pred->str_len = strlen(operand2); | 1169 | pred->regex.len = strlen(pred->regex.pattern); |
1047 | 1170 | ||
1048 | pred->op = op; | 1171 | pred->op = op; |
1049 | 1172 | ||