|
@@ -1329,6 +1329,9 @@ static struct filter_pred *create_pred(struct filter_parse_state *ps,
|
|
strcpy(pred.regex.pattern, operand2);
|
|
strcpy(pred.regex.pattern, operand2);
|
|
pred.regex.len = strlen(pred.regex.pattern);
|
|
pred.regex.len = strlen(pred.regex.pattern);
|
|
|
|
|
|
|
|
+#ifdef CONFIG_FTRACE_STARTUP_TEST
|
|
|
|
+ pred.field = field;
|
|
|
|
+#endif
|
|
return init_pred(ps, field, &pred) ? NULL : &pred;
|
|
return init_pred(ps, field, &pred) ? NULL : &pred;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1926,3 +1929,209 @@ out_unlock:
|
|
|
|
|
|
#endif /* CONFIG_PERF_EVENTS */
|
|
#endif /* CONFIG_PERF_EVENTS */
|
|
|
|
|
|
|
|
+#ifdef CONFIG_FTRACE_STARTUP_TEST
|
|
|
|
+
|
|
|
|
+#include <linux/types.h>
|
|
|
|
+#include <linux/tracepoint.h>
|
|
|
|
+
|
|
|
|
+#define CREATE_TRACE_POINTS
|
|
|
|
+#include "trace_events_filter_test.h"
|
|
|
|
+
|
|
|
|
+static int test_get_filter(char *filter_str, struct ftrace_event_call *call,
|
|
|
|
+ struct event_filter **pfilter)
|
|
|
|
+{
|
|
|
|
+ struct event_filter *filter;
|
|
|
|
+ struct filter_parse_state *ps;
|
|
|
|
+ int err = -ENOMEM;
|
|
|
|
+
|
|
|
|
+ filter = __alloc_filter();
|
|
|
|
+ if (!filter)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ps = kzalloc(sizeof(*ps), GFP_KERNEL);
|
|
|
|
+ if (!ps)
|
|
|
|
+ goto free_filter;
|
|
|
|
+
|
|
|
|
+ parse_init(ps, filter_ops, filter_str);
|
|
|
|
+ err = filter_parse(ps);
|
|
|
|
+ if (err)
|
|
|
|
+ goto free_ps;
|
|
|
|
+
|
|
|
|
+ err = replace_preds(call, filter, ps, filter_str, false);
|
|
|
|
+ if (!err)
|
|
|
|
+ *pfilter = filter;
|
|
|
|
+
|
|
|
|
+ free_ps:
|
|
|
|
+ filter_opstack_clear(ps);
|
|
|
|
+ postfix_clear(ps);
|
|
|
|
+ kfree(ps);
|
|
|
|
+
|
|
|
|
+ free_filter:
|
|
|
|
+ if (err)
|
|
|
|
+ __free_filter(filter);
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define DATA_REC(m, va, vb, vc, vd, ve, vf, vg, vh, nvisit) \
|
|
|
|
+{ \
|
|
|
|
+ .filter = FILTER, \
|
|
|
|
+ .rec = { .a = va, .b = vb, .c = vc, .d = vd, \
|
|
|
|
+ .e = ve, .f = vf, .g = vg, .h = vh }, \
|
|
|
|
+ .match = m, \
|
|
|
|
+ .not_visited = nvisit, \
|
|
|
|
+}
|
|
|
|
+#define YES 1
|
|
|
|
+#define NO 0
|
|
|
|
+
|
|
|
|
+static struct test_filter_data_t {
|
|
|
|
+ char *filter;
|
|
|
|
+ struct ftrace_raw_ftrace_test_filter rec;
|
|
|
|
+ int match;
|
|
|
|
+ char *not_visited;
|
|
|
|
+} test_filter_data[] = {
|
|
|
|
+#define FILTER "a == 1 && b == 1 && c == 1 && d == 1 && " \
|
|
|
|
+ "e == 1 && f == 1 && g == 1 && h == 1"
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, ""),
|
|
|
|
+ DATA_REC(NO, 0, 1, 1, 1, 1, 1, 1, 1, "bcdefgh"),
|
|
|
|
+ DATA_REC(NO, 1, 1, 1, 1, 1, 1, 1, 0, ""),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "a == 1 || b == 1 || c == 1 || d == 1 || " \
|
|
|
|
+ "e == 1 || f == 1 || g == 1 || h == 1"
|
|
|
|
+ DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""),
|
|
|
|
+ DATA_REC(YES, 0, 0, 0, 0, 0, 0, 0, 1, ""),
|
|
|
|
+ DATA_REC(YES, 1, 0, 0, 0, 0, 0, 0, 0, "bcdefgh"),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "(a == 1 || b == 1) && (c == 1 || d == 1) && " \
|
|
|
|
+ "(e == 1 || f == 1) && (g == 1 || h == 1)"
|
|
|
|
+ DATA_REC(NO, 0, 0, 1, 1, 1, 1, 1, 1, "dfh"),
|
|
|
|
+ DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""),
|
|
|
|
+ DATA_REC(YES, 1, 0, 1, 0, 0, 1, 0, 1, "bd"),
|
|
|
|
+ DATA_REC(NO, 1, 0, 1, 0, 0, 1, 0, 0, "bd"),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "(a == 1 && b == 1) || (c == 1 && d == 1) || " \
|
|
|
|
+ "(e == 1 && f == 1) || (g == 1 && h == 1)"
|
|
|
|
+ DATA_REC(YES, 1, 0, 1, 1, 1, 1, 1, 1, "efgh"),
|
|
|
|
+ DATA_REC(YES, 0, 0, 0, 0, 0, 0, 1, 1, ""),
|
|
|
|
+ DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "(a == 1 && b == 1) && (c == 1 && d == 1) && " \
|
|
|
|
+ "(e == 1 && f == 1) || (g == 1 && h == 1)"
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 0, "gh"),
|
|
|
|
+ DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""),
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, ""),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "((a == 1 || b == 1) || (c == 1 || d == 1) || " \
|
|
|
|
+ "(e == 1 || f == 1)) && (g == 1 || h == 1)"
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 1, "bcdef"),
|
|
|
|
+ DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""),
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, "h"),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "((((((((a == 1) && (b == 1)) || (c == 1)) && (d == 1)) || " \
|
|
|
|
+ "(e == 1)) && (f == 1)) || (g == 1)) && (h == 1))"
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "ceg"),
|
|
|
|
+ DATA_REC(NO, 0, 1, 0, 1, 0, 1, 0, 1, ""),
|
|
|
|
+ DATA_REC(NO, 1, 0, 1, 0, 1, 0, 1, 0, ""),
|
|
|
|
+#undef FILTER
|
|
|
|
+#define FILTER "((((((((a == 1) || (b == 1)) && (c == 1)) || (d == 1)) && " \
|
|
|
|
+ "(e == 1)) || (f == 1)) && (g == 1)) || (h == 1))"
|
|
|
|
+ DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "bdfh"),
|
|
|
|
+ DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""),
|
|
|
|
+ DATA_REC(YES, 1, 0, 1, 0, 1, 0, 1, 0, "bdfh"),
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#undef DATA_REC
|
|
|
|
+#undef FILTER
|
|
|
|
+#undef YES
|
|
|
|
+#undef NO
|
|
|
|
+
|
|
|
|
+#define DATA_CNT (sizeof(test_filter_data)/sizeof(struct test_filter_data_t))
|
|
|
|
+
|
|
|
|
+static int test_pred_visited;
|
|
|
|
+
|
|
|
|
+static int test_pred_visited_fn(struct filter_pred *pred, void *event)
|
|
|
|
+{
|
|
|
|
+ struct ftrace_event_field *field = pred->field;
|
|
|
|
+
|
|
|
|
+ test_pred_visited = 1;
|
|
|
|
+ printk(KERN_INFO "\npred visited %s\n", field->name);
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int test_walk_pred_cb(enum move_type move, struct filter_pred *pred,
|
|
|
|
+ int *err, void *data)
|
|
|
|
+{
|
|
|
|
+ char *fields = data;
|
|
|
|
+
|
|
|
|
+ if ((move == MOVE_DOWN) &&
|
|
|
|
+ (pred->left == FILTER_PRED_INVALID)) {
|
|
|
|
+ struct ftrace_event_field *field = pred->field;
|
|
|
|
+
|
|
|
|
+ if (!field) {
|
|
|
|
+ WARN(1, "all leafs should have field defined");
|
|
|
|
+ return WALK_PRED_DEFAULT;
|
|
|
|
+ }
|
|
|
|
+ if (!strchr(fields, *field->name))
|
|
|
|
+ return WALK_PRED_DEFAULT;
|
|
|
|
+
|
|
|
|
+ WARN_ON(!pred->fn);
|
|
|
|
+ pred->fn = test_pred_visited_fn;
|
|
|
|
+ }
|
|
|
|
+ return WALK_PRED_DEFAULT;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __init int ftrace_test_event_filter(void)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ printk(KERN_INFO "Testing ftrace filter: ");
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < DATA_CNT; i++) {
|
|
|
|
+ struct event_filter *filter = NULL;
|
|
|
|
+ struct test_filter_data_t *d = &test_filter_data[i];
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ err = test_get_filter(d->filter, &event_ftrace_test_filter,
|
|
|
|
+ &filter);
|
|
|
|
+ if (err) {
|
|
|
|
+ printk(KERN_INFO
|
|
|
|
+ "Failed to get filter for '%s', err %d\n",
|
|
|
|
+ d->filter, err);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (*d->not_visited)
|
|
|
|
+ walk_pred_tree(filter->preds, filter->root,
|
|
|
|
+ test_walk_pred_cb,
|
|
|
|
+ d->not_visited);
|
|
|
|
+
|
|
|
|
+ test_pred_visited = 0;
|
|
|
|
+ err = filter_match_preds(filter, &d->rec);
|
|
|
|
+
|
|
|
|
+ __free_filter(filter);
|
|
|
|
+
|
|
|
|
+ if (test_pred_visited) {
|
|
|
|
+ printk(KERN_INFO
|
|
|
|
+ "Failed, unwanted pred visited for filter %s\n",
|
|
|
|
+ d->filter);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (err != d->match) {
|
|
|
|
+ printk(KERN_INFO
|
|
|
|
+ "Failed to match filter '%s', expected %d\n",
|
|
|
|
+ d->filter, d->match);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i == DATA_CNT)
|
|
|
|
+ printk(KERN_CONT "OK\n");
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+late_initcall(ftrace_test_event_filter);
|
|
|
|
+
|
|
|
|
+#endif /* CONFIG_FTRACE_STARTUP_TEST */
|