diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2014-12-02 11:55:36 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2014-12-03 10:00:13 -0500 |
commit | e12c09cf3087b5a184ffeb55ca368e8aa436a3a2 (patch) | |
tree | edaefaee5f456a84484ccf40ae8b84f41228253f /kernel/trace | |
parent | 6a06bdbf7f9c669743f58084991ba280f2925586 (diff) |
tracing: Add NOT to filtering logic
Ted noticed that he could not filter on an event for a bit being cleared.
That's because the filtering logic only tests event fields with a limited
number of comparisons which, for bit logic, only include "&", which can
test if a bit is set, but there's no good way to see if a bit is clear.
This adds a way to do: !(field & 2048)
Which returns true if the bit is not set, and false otherwise.
Note, currently !(field1 == 10 && field2 == 15) is not supported.
That is, the 'not' only works for direct comparisons, not for the
AND and OR logic.
Link: http://lkml.kernel.org/r/20141202021912.GA29096@thunk.org
Link: http://lkml.kernel.org/r/20141202120430.71979060@gandalf.local.home
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Suggested-by: "Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace_events_filter.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 7a8c1528e141..e6a33db83856 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -45,6 +45,7 @@ enum filter_op_ids | |||
45 | OP_GT, | 45 | OP_GT, |
46 | OP_GE, | 46 | OP_GE, |
47 | OP_BAND, | 47 | OP_BAND, |
48 | OP_NOT, | ||
48 | OP_NONE, | 49 | OP_NONE, |
49 | OP_OPEN_PAREN, | 50 | OP_OPEN_PAREN, |
50 | }; | 51 | }; |
@@ -67,6 +68,7 @@ static struct filter_op filter_ops[] = { | |||
67 | { OP_GT, ">", 5 }, | 68 | { OP_GT, ">", 5 }, |
68 | { OP_GE, ">=", 5 }, | 69 | { OP_GE, ">=", 5 }, |
69 | { OP_BAND, "&", 6 }, | 70 | { OP_BAND, "&", 6 }, |
71 | { OP_NOT, "!", 6 }, | ||
70 | { OP_NONE, "OP_NONE", 0 }, | 72 | { OP_NONE, "OP_NONE", 0 }, |
71 | { OP_OPEN_PAREN, "(", 0 }, | 73 | { OP_OPEN_PAREN, "(", 0 }, |
72 | }; | 74 | }; |
@@ -85,6 +87,7 @@ enum { | |||
85 | FILT_ERR_MISSING_FIELD, | 87 | FILT_ERR_MISSING_FIELD, |
86 | FILT_ERR_INVALID_FILTER, | 88 | FILT_ERR_INVALID_FILTER, |
87 | FILT_ERR_IP_FIELD_ONLY, | 89 | FILT_ERR_IP_FIELD_ONLY, |
90 | FILT_ERR_ILLEGAL_NOT_OP, | ||
88 | }; | 91 | }; |
89 | 92 | ||
90 | static char *err_text[] = { | 93 | static char *err_text[] = { |
@@ -101,6 +104,7 @@ static char *err_text[] = { | |||
101 | "Missing field name and/or value", | 104 | "Missing field name and/or value", |
102 | "Meaningless filter expression", | 105 | "Meaningless filter expression", |
103 | "Only 'ip' field is supported for function trace", | 106 | "Only 'ip' field is supported for function trace", |
107 | "Illegal use of '!'", | ||
104 | }; | 108 | }; |
105 | 109 | ||
106 | struct opstack_op { | 110 | struct opstack_op { |
@@ -139,6 +143,7 @@ struct pred_stack { | |||
139 | int index; | 143 | int index; |
140 | }; | 144 | }; |
141 | 145 | ||
146 | /* If not of not match is equal to not of not, then it is a match */ | ||
142 | #define DEFINE_COMPARISON_PRED(type) \ | 147 | #define DEFINE_COMPARISON_PRED(type) \ |
143 | static int filter_pred_##type(struct filter_pred *pred, void *event) \ | 148 | static int filter_pred_##type(struct filter_pred *pred, void *event) \ |
144 | { \ | 149 | { \ |
@@ -166,7 +171,7 @@ static int filter_pred_##type(struct filter_pred *pred, void *event) \ | |||
166 | break; \ | 171 | break; \ |
167 | } \ | 172 | } \ |
168 | \ | 173 | \ |
169 | return match; \ | 174 | return !!match == !pred->not; \ |
170 | } | 175 | } |
171 | 176 | ||
172 | #define DEFINE_EQUALITY_PRED(size) \ | 177 | #define DEFINE_EQUALITY_PRED(size) \ |
@@ -1028,7 +1033,7 @@ static int init_pred(struct filter_parse_state *ps, | |||
1028 | } | 1033 | } |
1029 | 1034 | ||
1030 | if (pred->op == OP_NE) | 1035 | if (pred->op == OP_NE) |
1031 | pred->not = 1; | 1036 | pred->not ^= 1; |
1032 | 1037 | ||
1033 | pred->fn = fn; | 1038 | pred->fn = fn; |
1034 | return 0; | 1039 | return 0; |
@@ -1590,6 +1595,17 @@ static int replace_preds(struct ftrace_event_call *call, | |||
1590 | continue; | 1595 | continue; |
1591 | } | 1596 | } |
1592 | 1597 | ||
1598 | if (elt->op == OP_NOT) { | ||
1599 | if (!n_preds || operand1 || operand2) { | ||
1600 | parse_error(ps, FILT_ERR_ILLEGAL_NOT_OP, 0); | ||
1601 | err = -EINVAL; | ||
1602 | goto fail; | ||
1603 | } | ||
1604 | if (!dry_run) | ||
1605 | filter->preds[n_preds - 1].not ^= 1; | ||
1606 | continue; | ||
1607 | } | ||
1608 | |||
1593 | if (WARN_ON(n_preds++ == MAX_FILTER_PRED)) { | 1609 | if (WARN_ON(n_preds++ == MAX_FILTER_PRED)) { |
1594 | parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0); | 1610 | parse_error(ps, FILT_ERR_TOO_MANY_PREDS, 0); |
1595 | err = -ENOSPC; | 1611 | err = -ENOSPC; |