]> Pileus Git - ~andy/linux/blobdiff - kernel/trace/trace_events_filter.c
tracing/filter: Add startup tests for events filter
[~andy/linux] / kernel / trace / trace_events_filter.c
index 319c3cac7d95e14f36476321a1708ed729911f23..6a642e2782411de6f8d891d4dde7f8df136d21e6 100644 (file)
@@ -1329,6 +1329,9 @@ static struct filter_pred *create_pred(struct filter_parse_state *ps,
        strcpy(pred.regex.pattern, operand2);
        pred.regex.len = strlen(pred.regex.pattern);
 
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+       pred.field = field;
+#endif
        return init_pred(ps, field, &pred) ? NULL : &pred;
 }
 
@@ -1926,3 +1929,209 @@ out_unlock:
 
 #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 */