mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
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:
parent
a118e4d140
commit
8b37256210
4 changed files with 898 additions and 304 deletions
|
@ -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);
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue