tracing/filter: Add startup tests for events filter
authorJiri Olsa <jolsa@redhat.com>
Thu, 11 Aug 2011 14:25:54 +0000 (16:25 +0200)
committerSteven Rostedt <rostedt@goodmis.org>
Fri, 19 Aug 2011 18:35:59 +0000 (14:35 -0400)
Adding automated tests running as late_initcall. Tests are
compiled in with CONFIG_FTRACE_STARTUP_TEST option.

Adding test event "ftrace_test_filter" used to simulate
filter processing during event occurance.

String filters are compiled and tested against several
test events with different values.

Also testing that evaluation of explicit predicates is ommited
due to the lazy filter evaluation.

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/1313072754-4620-11-git-send-email-jolsa@redhat.com
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/Makefile
kernel/trace/trace.h
kernel/trace/trace_events_filter.c
kernel/trace/trace_events_filter_test.h [new file with mode: 0644]

index 761c510a06c5989ac7e03be55991a1ac346b4db2..b384ed512bac115da6097ea66ea003e4843be63a 100644 (file)
@@ -15,6 +15,8 @@ ifdef CONFIG_TRACING_BRANCHES
 KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
 endif
 
+CFLAGS_trace_events_filter.o := -I$(src)
+
 #
 # Make the trace clocks available generally: it's infrastructure
 # relied on by ptrace for example:
index 2eb3cf6d37bcaf85b53bc597bcc5f312bc301518..4c7540ad5dcb43c2250f13b12785ff6ad5c4dd64 100644 (file)
@@ -762,6 +762,9 @@ struct filter_pred {
        u64                     val;
        struct regex            regex;
        unsigned short          *ops;
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+       struct ftrace_event_field *field;
+#endif
        int                     offset;
        int                     not;
        int                     op;
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 */
diff --git a/kernel/trace/trace_events_filter_test.h b/kernel/trace/trace_events_filter_test.h
new file mode 100644 (file)
index 0000000..bfd4dba
--- /dev/null
@@ -0,0 +1,50 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM test
+
+#if !defined(_TRACE_TEST_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TEST_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(ftrace_test_filter,
+
+       TP_PROTO(int a, int b, int c, int d, int e, int f, int g, int h),
+
+       TP_ARGS(a, b, c, d, e, f, g, h),
+
+       TP_STRUCT__entry(
+               __field(int, a)
+               __field(int, b)
+               __field(int, c)
+               __field(int, d)
+               __field(int, e)
+               __field(int, f)
+               __field(int, g)
+               __field(int, h)
+       ),
+
+       TP_fast_assign(
+               __entry->a = a;
+               __entry->b = b;
+               __entry->c = c;
+               __entry->d = d;
+               __entry->e = e;
+               __entry->f = f;
+               __entry->g = g;
+               __entry->h = h;
+       ),
+
+       TP_printk("a %d, b %d, c %d, d %d, e %d, f %d, g %d, h %d",
+                 __entry->a, __entry->b, __entry->c, __entry->d,
+                 __entry->e, __entry->f, __entry->g, __entry->h)
+);
+
+#endif /* _TRACE_TEST_H || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace_events_filter_test
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>