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; |
