diff options
author | Tom Zanussi <tzanussi@gmail.com> | 2009-03-22 04:31:04 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-03-22 13:38:46 -0400 |
commit | 7ce7e4249921d5073e764f7ff7ad83cfa9894bd7 (patch) | |
tree | d8a3026e85d3230ce39ca99f446abe76a710c337 /kernel/trace/trace_events.c | |
parent | 2d622719f1572ef31e0616444a515eba3094d050 (diff) |
tracing: add per-event filtering
This patch adds per-event filtering to the event tracing subsystem.
It adds a 'filter' debugfs file to each event directory. This file can
be written to to set filters; reading from it will display the current
set of filters set for that event.
Basically, any field listed in the 'format' file for an event can be
filtered on (including strings, but not yet other array types) using
either matching ('==') or non-matching ('!=') 'predicates'. A
'predicate' can be either a single expression:
# echo pid != 0 > filter
# cat filter
pid != 0
or a compound expression of up to 8 sub-expressions combined using '&&'
or '||':
# echo comm == Xorg > filter
# echo "&& sig != 29" > filter
# cat filter
comm == Xorg
&& sig != 29
Only events having field values matching an expression will be available
in the trace output; non-matching events are discarded.
Note that a compound expression is built up by echoing each
sub-expression separately - it's not the most efficient way to do
things, but it keeps the parser simple and assumes that compound
expressions will be relatively uncommon. In any case, a subsequent
patch introducing a way to set filters for entire subsystems should
mitigate any need to do this for lots of events.
Setting a filter without an '&&' or '||' clears the previous filter
completely and sets the filter to the new expression:
# cat filter
comm == Xorg
&& sig != 29
# echo comm != Xorg
# cat filter
comm != Xorg
To clear a filter, echo 0 to the filter file:
# echo 0 > filter
# cat filter
none
The limit of 8 predicates for a compound expression is arbitrary - for
efficiency, it's implemented as an array of pointers to predicates, and
8 seemed more than enough for any filter...
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1237710665.7703.48.camel@charm-linux>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace/trace_events.c')
-rw-r--r-- | kernel/trace/trace_events.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 961b057da28b..97470c48956e 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -459,6 +459,71 @@ event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) | |||
459 | return r; | 459 | return r; |
460 | } | 460 | } |
461 | 461 | ||
462 | static ssize_t | ||
463 | event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, | ||
464 | loff_t *ppos) | ||
465 | { | ||
466 | struct ftrace_event_call *call = filp->private_data; | ||
467 | struct trace_seq *s; | ||
468 | int r; | ||
469 | |||
470 | if (*ppos) | ||
471 | return 0; | ||
472 | |||
473 | s = kmalloc(sizeof(*s), GFP_KERNEL); | ||
474 | if (!s) | ||
475 | return -ENOMEM; | ||
476 | |||
477 | trace_seq_init(s); | ||
478 | |||
479 | r = filter_print_preds(call->preds, s->buffer); | ||
480 | r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, r); | ||
481 | |||
482 | kfree(s); | ||
483 | |||
484 | return r; | ||
485 | } | ||
486 | |||
487 | static ssize_t | ||
488 | event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, | ||
489 | loff_t *ppos) | ||
490 | { | ||
491 | struct ftrace_event_call *call = filp->private_data; | ||
492 | char buf[64], *pbuf = buf; | ||
493 | struct filter_pred *pred; | ||
494 | int err; | ||
495 | |||
496 | if (cnt >= sizeof(buf)) | ||
497 | return -EINVAL; | ||
498 | |||
499 | if (copy_from_user(&buf, ubuf, cnt)) | ||
500 | return -EFAULT; | ||
501 | |||
502 | pred = kzalloc(sizeof(*pred), GFP_KERNEL); | ||
503 | if (!pred) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | err = filter_parse(&pbuf, pred); | ||
507 | if (err < 0) { | ||
508 | filter_free_pred(pred); | ||
509 | return err; | ||
510 | } | ||
511 | |||
512 | if (pred->clear) { | ||
513 | filter_free_preds(call); | ||
514 | return cnt; | ||
515 | } | ||
516 | |||
517 | if (filter_add_pred(call, pred)) { | ||
518 | filter_free_pred(pred); | ||
519 | return -EINVAL; | ||
520 | } | ||
521 | |||
522 | *ppos += cnt; | ||
523 | |||
524 | return cnt; | ||
525 | } | ||
526 | |||
462 | static const struct seq_operations show_event_seq_ops = { | 527 | static const struct seq_operations show_event_seq_ops = { |
463 | .start = t_start, | 528 | .start = t_start, |
464 | .next = t_next, | 529 | .next = t_next, |
@@ -504,6 +569,12 @@ static const struct file_operations ftrace_event_id_fops = { | |||
504 | .read = event_id_read, | 569 | .read = event_id_read, |
505 | }; | 570 | }; |
506 | 571 | ||
572 | static const struct file_operations ftrace_event_filter_fops = { | ||
573 | .open = tracing_open_generic, | ||
574 | .read = event_filter_read, | ||
575 | .write = event_filter_write, | ||
576 | }; | ||
577 | |||
507 | static struct dentry *event_trace_events_dir(void) | 578 | static struct dentry *event_trace_events_dir(void) |
508 | { | 579 | { |
509 | static struct dentry *d_tracer; | 580 | static struct dentry *d_tracer; |
@@ -619,6 +690,12 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
619 | } | 690 | } |
620 | } | 691 | } |
621 | 692 | ||
693 | entry = debugfs_create_file("filter", 0444, call->dir, call, | ||
694 | &ftrace_event_filter_fops); | ||
695 | if (!entry) | ||
696 | pr_warning("Could not create debugfs " | ||
697 | "'%s/filter' entry\n", call->name); | ||
698 | |||
622 | /* A trace may not want to export its format */ | 699 | /* A trace may not want to export its format */ |
623 | if (!call->show_format) | 700 | if (!call->show_format) |
624 | return 0; | 701 | return 0; |