tracing/filters: a better event parser

Replace the current event parser hack with a better one.  Filters are
no longer specified predicate by predicate, but all at once and can
use parens and any of the following operators:

numeric fields:

==, !=, <, <=, >, >=

string fields:

==, !=

predicates can be combined with the logical operators:

&&, ||

examples:

"common_preempt_count > 4" > filter

"((sig >= 10 && sig < 15) || sig == 17) && comm != bash" > filter

If there was an error, the erroneous string along with an error
message can be seen by looking at the filter e.g.:

((sig >= 10 && sig < 15) || dsig == 17) && comm != bash
^
parse_error: Field not found

Currently the caret for an error always appears at the beginning of
the filter; a real position should be used, but the error message
should be useful even without it.

To clear a filter, '0' can be written to the filter file.

Filters can also be set or cleared for a complete subsystem by writing
the same filter as would be written to an individual event to the
filter file at the root of the subsytem.  Note however, that if any
event in the subsystem lacks a field specified in the filter being
set, the set will fail and all filters in the subsytem are
automatically cleared.  This change from the previous version was made
because using only the fields that happen to exist for a given event
would most likely result in a meaningless filter.

Because the logical operators are now implemented as predicates, the
maximum number of predicates in a filter was increased from 8 to 16.

[ Impact: add new, extended trace-filter implementation ]

Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Cc: fweisbec@gmail.com
Cc: Li Zefan <lizf@cn.fujitsu.com>
LKML-Reference: <1240905899.6416.121.camel@tropicana>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Tom Zanussi 2009-04-28 03:04:59 -05:00 committed by Ingo Molnar
parent a118e4d140
commit 8b37256210
4 changed files with 898 additions and 304 deletions

View File

@ -112,7 +112,7 @@ struct ftrace_event_call {
#endif
};
#define MAX_FILTER_PRED 8
#define MAX_FILTER_PRED 32
#define MAX_FILTER_STR_VAL 128
extern int init_preds(struct ftrace_event_call *call);

View File

@ -735,6 +735,7 @@ struct ftrace_event_field {
struct event_filter {
int n_preds;
struct filter_pred **preds;
char *filter_string;
};
struct event_subsystem {
@ -746,7 +747,8 @@ struct event_subsystem {
struct filter_pred;
typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event);
typedef int (*filter_pred_fn_t) (struct filter_pred *pred, void *event,
int val1, int val2);
struct filter_pred {
filter_pred_fn_t fn;
@ -756,23 +758,18 @@ struct filter_pred {
char *field_name;
int offset;
int not;
int or;
int compound;
int clear;
int op;
int pop_n;
};
extern void filter_free_pred(struct filter_pred *pred);
extern void filter_print_preds(struct ftrace_event_call *call,
extern void print_event_filter(struct ftrace_event_call *call,
struct trace_seq *s);
extern int filter_parse(char **pbuf, struct filter_pred *pred);
extern int filter_add_pred(struct ftrace_event_call *call,
struct filter_pred *pred);
extern void filter_disable_preds(struct ftrace_event_call *call);
extern void filter_free_subsystem_preds(struct event_subsystem *system);
extern void filter_print_subsystem_preds(struct event_subsystem *system,
extern int apply_event_filter(struct ftrace_event_call *call,
char *filter_string);
extern int apply_subsystem_event_filter(struct event_subsystem *system,
char *filter_string);
extern void print_subsystem_event_filter(struct event_subsystem *system,
struct trace_seq *s);
extern int filter_add_subsystem_pred(struct event_subsystem *system,
struct filter_pred *pred);
static inline int
filter_check_discard(struct ftrace_event_call *call, void *rec,
@ -787,6 +784,47 @@ filter_check_discard(struct ftrace_event_call *call, void *rec,
return 0;
}
#define DEFINE_COMPARISON_PRED(type) \
static int filter_pred_##type(struct filter_pred *pred, void *event, \
int val1, int val2) \
{ \
type *addr = (type *)(event + pred->offset); \
type val = (type)pred->val; \
int match = 0; \
\
switch (pred->op) { \
case OP_LT: \
match = (*addr < val); \
break; \
case OP_LE: \
match = (*addr <= val); \
break; \
case OP_GT: \
match = (*addr > val); \
break; \
case OP_GE: \
match = (*addr >= val); \
break; \
default: \
break; \
} \
\
return match; \
}
#define DEFINE_EQUALITY_PRED(size) \
static int filter_pred_##size(struct filter_pred *pred, void *event, \
int val1, int val2) \
{ \
u##size *addr = (u##size *)(event + pred->offset); \
u##size val = (u##size)pred->val; \
int match; \
\
match = (val == *addr) ^ pred->not; \
\
return match; \
}
extern struct list_head ftrace_events;
extern const char *__start___trace_bprintk_fmt[];

View File

@ -492,7 +492,7 @@ event_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
trace_seq_init(s);
filter_print_preds(call, s);
print_event_filter(call, s);
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
kfree(s);
@ -505,40 +505,26 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct ftrace_event_call *call = filp->private_data;
char buf[64], *pbuf = buf;
struct filter_pred *pred;
char *buf;
int err;
if (cnt >= sizeof(buf))
if (cnt >= PAGE_SIZE)
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = '\0';
pred = kzalloc(sizeof(*pred), GFP_KERNEL);
if (!pred)
buf = (char *)__get_free_page(GFP_TEMPORARY);
if (!buf)
return -ENOMEM;
err = filter_parse(&pbuf, pred);
if (err < 0) {
filter_free_pred(pred);
if (copy_from_user(buf, ubuf, cnt)) {
free_page((unsigned long) buf);
return -EFAULT;
}
buf[cnt] = '\0';
err = apply_event_filter(call, buf);
free_page((unsigned long) buf);
if (err < 0)
return err;
}
if (pred->clear) {
filter_disable_preds(call);
filter_free_pred(pred);
return cnt;
}
err = filter_add_pred(call, pred);
if (err < 0) {
filter_free_pred(pred);
return err;
}
filter_free_pred(pred);
*ppos += cnt;
@ -562,7 +548,7 @@ subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
trace_seq_init(s);
filter_print_subsystem_preds(system, s);
print_subsystem_event_filter(system, s);
r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len);
kfree(s);
@ -575,38 +561,26 @@ subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
loff_t *ppos)
{
struct event_subsystem *system = filp->private_data;
char buf[64], *pbuf = buf;
struct filter_pred *pred;
char *buf;
int err;
if (cnt >= sizeof(buf))
if (cnt >= PAGE_SIZE)
return -EINVAL;
if (copy_from_user(&buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = '\0';
pred = kzalloc(sizeof(*pred), GFP_KERNEL);
if (!pred)
buf = (char *)__get_free_page(GFP_TEMPORARY);
if (!buf)
return -ENOMEM;
err = filter_parse(&pbuf, pred);
if (err < 0) {
filter_free_pred(pred);
return err;
if (copy_from_user(buf, ubuf, cnt)) {
free_page((unsigned long) buf);
return -EFAULT;
}
buf[cnt] = '\0';
if (pred->clear) {
filter_free_subsystem_preds(system);
filter_free_pred(pred);
return cnt;
}
err = filter_add_subsystem_pred(system, pred);
if (err < 0) {
filter_free_pred(pred);
err = apply_subsystem_event_filter(system, buf);
free_page((unsigned long) buf);
if (err < 0)
return err;
}
*ppos += cnt;
@ -760,11 +734,21 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
system->filter = NULL;
system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL);
if (!system->filter) {
pr_warning("Could not allocate filter for subsystem "
"'%s'\n", name);
return system->entry;
}
entry = debugfs_create_file("filter", 0644, system->entry, system,
&ftrace_subsystem_filter_fops);
if (!entry)
if (!entry) {
kfree(system->filter);
system->filter = NULL;
pr_warning("Could not create debugfs "
"'%s/filter' entry\n", name);
}
return system->entry;
}

File diff suppressed because it is too large Load Diff