diff options
-rw-r--r-- | kernel/trace/Makefile | 2 | ||||
-rw-r--r-- | kernel/trace/trace.h | 3 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 209 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter_test.h | 50 |
4 files changed, 264 insertions, 0 deletions
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 761c510a06c5..b384ed512bac 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -15,6 +15,8 @@ ifdef CONFIG_TRACING_BRANCHES | |||
15 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING | 15 | KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING |
16 | endif | 16 | endif |
17 | 17 | ||
18 | CFLAGS_trace_events_filter.o := -I$(src) | ||
19 | |||
18 | # | 20 | # |
19 | # Make the trace clocks available generally: it's infrastructure | 21 | # Make the trace clocks available generally: it's infrastructure |
20 | # relied on by ptrace for example: | 22 | # relied on by ptrace for example: |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2eb3cf6d37bc..4c7540ad5dcb 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -762,6 +762,9 @@ struct filter_pred { | |||
762 | u64 val; | 762 | u64 val; |
763 | struct regex regex; | 763 | struct regex regex; |
764 | unsigned short *ops; | 764 | unsigned short *ops; |
765 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
766 | struct ftrace_event_field *field; | ||
767 | #endif | ||
765 | int offset; | 768 | int offset; |
766 | int not; | 769 | int not; |
767 | int op; | 770 | int op; |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 319c3cac7d95..6a642e278241 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -1329,6 +1329,9 @@ static struct filter_pred *create_pred(struct filter_parse_state *ps, | |||
1329 | strcpy(pred.regex.pattern, operand2); | 1329 | strcpy(pred.regex.pattern, operand2); |
1330 | pred.regex.len = strlen(pred.regex.pattern); | 1330 | pred.regex.len = strlen(pred.regex.pattern); |
1331 | 1331 | ||
1332 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
1333 | pred.field = field; | ||
1334 | #endif | ||
1332 | return init_pred(ps, field, &pred) ? NULL : &pred; | 1335 | return init_pred(ps, field, &pred) ? NULL : &pred; |
1333 | } | 1336 | } |
1334 | 1337 | ||
@@ -1926,3 +1929,209 @@ out_unlock: | |||
1926 | 1929 | ||
1927 | #endif /* CONFIG_PERF_EVENTS */ | 1930 | #endif /* CONFIG_PERF_EVENTS */ |
1928 | 1931 | ||
1932 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
1933 | |||
1934 | #include <linux/types.h> | ||
1935 | #include <linux/tracepoint.h> | ||
1936 | |||
1937 | #define CREATE_TRACE_POINTS | ||
1938 | #include "trace_events_filter_test.h" | ||
1939 | |||
1940 | static int test_get_filter(char *filter_str, struct ftrace_event_call *call, | ||
1941 | struct event_filter **pfilter) | ||
1942 | { | ||
1943 | struct event_filter *filter; | ||
1944 | struct filter_parse_state *ps; | ||
1945 | int err = -ENOMEM; | ||
1946 | |||
1947 | filter = __alloc_filter(); | ||
1948 | if (!filter) | ||
1949 | goto out; | ||
1950 | |||
1951 | ps = kzalloc(sizeof(*ps), GFP_KERNEL); | ||
1952 | if (!ps) | ||
1953 | goto free_filter; | ||
1954 | |||
1955 | parse_init(ps, filter_ops, filter_str); | ||
1956 | err = filter_parse(ps); | ||
1957 | if (err) | ||
1958 | goto free_ps; | ||
1959 | |||
1960 | err = replace_preds(call, filter, ps, filter_str, false); | ||
1961 | if (!err) | ||
1962 | *pfilter = filter; | ||
1963 | |||
1964 | free_ps: | ||
1965 | filter_opstack_clear(ps); | ||
1966 | postfix_clear(ps); | ||
1967 | kfree(ps); | ||
1968 | |||
1969 | free_filter: | ||
1970 | if (err) | ||
1971 | __free_filter(filter); | ||
1972 | |||
1973 | out: | ||
1974 | return err; | ||
1975 | } | ||
1976 | |||
1977 | #define DATA_REC(m, va, vb, vc, vd, ve, vf, vg, vh, nvisit) \ | ||
1978 | { \ | ||
1979 | .filter = FILTER, \ | ||
1980 | .rec = { .a = va, .b = vb, .c = vc, .d = vd, \ | ||
1981 | .e = ve, .f = vf, .g = vg, .h = vh }, \ | ||
1982 | .match = m, \ | ||
1983 | .not_visited = nvisit, \ | ||
1984 | } | ||
1985 | #define YES 1 | ||
1986 | #define NO 0 | ||
1987 | |||
1988 | static struct test_filter_data_t { | ||
1989 | char *filter; | ||
1990 | struct ftrace_raw_ftrace_test_filter rec; | ||
1991 | int match; | ||
1992 | char *not_visited; | ||
1993 | } test_filter_data[] = { | ||
1994 | #define FILTER "a == 1 && b == 1 && c == 1 && d == 1 && " \ | ||
1995 | "e == 1 && f == 1 && g == 1 && h == 1" | ||
1996 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, ""), | ||
1997 | DATA_REC(NO, 0, 1, 1, 1, 1, 1, 1, 1, "bcdefgh"), | ||
1998 | DATA_REC(NO, 1, 1, 1, 1, 1, 1, 1, 0, ""), | ||
1999 | #undef FILTER | ||
2000 | #define FILTER "a == 1 || b == 1 || c == 1 || d == 1 || " \ | ||
2001 | "e == 1 || f == 1 || g == 1 || h == 1" | ||
2002 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""), | ||
2003 | DATA_REC(YES, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2004 | DATA_REC(YES, 1, 0, 0, 0, 0, 0, 0, 0, "bcdefgh"), | ||
2005 | #undef FILTER | ||
2006 | #define FILTER "(a == 1 || b == 1) && (c == 1 || d == 1) && " \ | ||
2007 | "(e == 1 || f == 1) && (g == 1 || h == 1)" | ||
2008 | DATA_REC(NO, 0, 0, 1, 1, 1, 1, 1, 1, "dfh"), | ||
2009 | DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2010 | DATA_REC(YES, 1, 0, 1, 0, 0, 1, 0, 1, "bd"), | ||
2011 | DATA_REC(NO, 1, 0, 1, 0, 0, 1, 0, 0, "bd"), | ||
2012 | #undef FILTER | ||
2013 | #define FILTER "(a == 1 && b == 1) || (c == 1 && d == 1) || " \ | ||
2014 | "(e == 1 && f == 1) || (g == 1 && h == 1)" | ||
2015 | DATA_REC(YES, 1, 0, 1, 1, 1, 1, 1, 1, "efgh"), | ||
2016 | DATA_REC(YES, 0, 0, 0, 0, 0, 0, 1, 1, ""), | ||
2017 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2018 | #undef FILTER | ||
2019 | #define FILTER "(a == 1 && b == 1) && (c == 1 && d == 1) && " \ | ||
2020 | "(e == 1 && f == 1) || (g == 1 && h == 1)" | ||
2021 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 0, "gh"), | ||
2022 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 1, ""), | ||
2023 | DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, ""), | ||
2024 | #undef FILTER | ||
2025 | #define FILTER "((a == 1 || b == 1) || (c == 1 || d == 1) || " \ | ||
2026 | "(e == 1 || f == 1)) && (g == 1 || h == 1)" | ||
2027 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 0, 1, "bcdef"), | ||
2028 | DATA_REC(NO, 0, 0, 0, 0, 0, 0, 0, 0, ""), | ||
2029 | DATA_REC(YES, 1, 1, 1, 1, 1, 0, 1, 1, "h"), | ||
2030 | #undef FILTER | ||
2031 | #define FILTER "((((((((a == 1) && (b == 1)) || (c == 1)) && (d == 1)) || " \ | ||
2032 | "(e == 1)) && (f == 1)) || (g == 1)) && (h == 1))" | ||
2033 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "ceg"), | ||
2034 | DATA_REC(NO, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2035 | DATA_REC(NO, 1, 0, 1, 0, 1, 0, 1, 0, ""), | ||
2036 | #undef FILTER | ||
2037 | #define FILTER "((((((((a == 1) || (b == 1)) && (c == 1)) || (d == 1)) && " \ | ||
2038 | "(e == 1)) || (f == 1)) && (g == 1)) || (h == 1))" | ||
2039 | DATA_REC(YES, 1, 1, 1, 1, 1, 1, 1, 1, "bdfh"), | ||
2040 | DATA_REC(YES, 0, 1, 0, 1, 0, 1, 0, 1, ""), | ||
2041 | DATA_REC(YES, 1, 0, 1, 0, 1, 0, 1, 0, "bdfh"), | ||
2042 | }; | ||
2043 | |||
2044 | #undef DATA_REC | ||
2045 | #undef FILTER | ||
2046 | #undef YES | ||
2047 | #undef NO | ||
2048 | |||
2049 | #define DATA_CNT (sizeof(test_filter_data)/sizeof(struct test_filter_data_t)) | ||
2050 | |||
2051 | static int test_pred_visited; | ||
2052 | |||
2053 | static int test_pred_visited_fn(struct filter_pred *pred, void *event) | ||
2054 | { | ||
2055 | struct ftrace_event_field *field = pred->field; | ||
2056 | |||
2057 | test_pred_visited = 1; | ||
2058 | printk(KERN_INFO "\npred visited %s\n", field->name); | ||
2059 | return 1; | ||
2060 | } | ||
2061 | |||
2062 | static int test_walk_pred_cb(enum move_type move, struct filter_pred *pred, | ||
2063 | int *err, void *data) | ||
2064 | { | ||
2065 | char *fields = data; | ||
2066 | |||
2067 | if ((move == MOVE_DOWN) && | ||
2068 | (pred->left == FILTER_PRED_INVALID)) { | ||
2069 | struct ftrace_event_field *field = pred->field; | ||
2070 | |||
2071 | if (!field) { | ||
2072 | WARN(1, "all leafs should have field defined"); | ||
2073 | return WALK_PRED_DEFAULT; | ||
2074 | } | ||
2075 | if (!strchr(fields, *field->name)) | ||
2076 | return WALK_PRED_DEFAULT; | ||
2077 | |||
2078 | WARN_ON(!pred->fn); | ||
2079 | pred->fn = test_pred_visited_fn; | ||
2080 | } | ||
2081 | return WALK_PRED_DEFAULT; | ||
2082 | } | ||
2083 | |||
2084 | static __init int ftrace_test_event_filter(void) | ||
2085 | { | ||
2086 | int i; | ||
2087 | |||
2088 | printk(KERN_INFO "Testing ftrace filter: "); | ||
2089 | |||
2090 | for (i = 0; i < DATA_CNT; i++) { | ||
2091 | struct event_filter *filter = NULL; | ||
2092 | struct test_filter_data_t *d = &test_filter_data[i]; | ||
2093 | int err; | ||
2094 | |||
2095 | err = test_get_filter(d->filter, &event_ftrace_test_filter, | ||
2096 | &filter); | ||
2097 | if (err) { | ||
2098 | printk(KERN_INFO | ||
2099 | "Failed to get filter for '%s', err %d\n", | ||
2100 | d->filter, err); | ||
2101 | break; | ||
2102 | } | ||
2103 | |||
2104 | if (*d->not_visited) | ||
2105 | walk_pred_tree(filter->preds, filter->root, | ||
2106 | test_walk_pred_cb, | ||
2107 | d->not_visited); | ||
2108 | |||
2109 | test_pred_visited = 0; | ||
2110 | err = filter_match_preds(filter, &d->rec); | ||
2111 | |||
2112 | __free_filter(filter); | ||
2113 | |||
2114 | if (test_pred_visited) { | ||
2115 | printk(KERN_INFO | ||
2116 | "Failed, unwanted pred visited for filter %s\n", | ||
2117 | d->filter); | ||
2118 | break; | ||
2119 | } | ||
2120 | |||
2121 | if (err != d->match) { | ||
2122 | printk(KERN_INFO | ||
2123 | "Failed to match filter '%s', expected %d\n", | ||
2124 | d->filter, d->match); | ||
2125 | break; | ||
2126 | } | ||
2127 | } | ||
2128 | |||
2129 | if (i == DATA_CNT) | ||
2130 | printk(KERN_CONT "OK\n"); | ||
2131 | |||
2132 | return 0; | ||
2133 | } | ||
2134 | |||
2135 | late_initcall(ftrace_test_event_filter); | ||
2136 | |||
2137 | #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 index 000000000000..bfd4dba0d603 --- /dev/null +++ b/kernel/trace/trace_events_filter_test.h | |||
@@ -0,0 +1,50 @@ | |||
1 | #undef TRACE_SYSTEM | ||
2 | #define TRACE_SYSTEM test | ||
3 | |||
4 | #if !defined(_TRACE_TEST_H) || defined(TRACE_HEADER_MULTI_READ) | ||
5 | #define _TRACE_TEST_H | ||
6 | |||
7 | #include <linux/tracepoint.h> | ||
8 | |||
9 | TRACE_EVENT(ftrace_test_filter, | ||
10 | |||
11 | TP_PROTO(int a, int b, int c, int d, int e, int f, int g, int h), | ||
12 | |||
13 | TP_ARGS(a, b, c, d, e, f, g, h), | ||
14 | |||
15 | TP_STRUCT__entry( | ||
16 | __field(int, a) | ||
17 | __field(int, b) | ||
18 | __field(int, c) | ||
19 | __field(int, d) | ||
20 | __field(int, e) | ||
21 | __field(int, f) | ||
22 | __field(int, g) | ||
23 | __field(int, h) | ||
24 | ), | ||
25 | |||
26 | TP_fast_assign( | ||
27 | __entry->a = a; | ||
28 | __entry->b = b; | ||
29 | __entry->c = c; | ||
30 | __entry->d = d; | ||
31 | __entry->e = e; | ||
32 | __entry->f = f; | ||
33 | __entry->g = g; | ||
34 | __entry->h = h; | ||
35 | ), | ||
36 | |||
37 | TP_printk("a %d, b %d, c %d, d %d, e %d, f %d, g %d, h %d", | ||
38 | __entry->a, __entry->b, __entry->c, __entry->d, | ||
39 | __entry->e, __entry->f, __entry->g, __entry->h) | ||
40 | ); | ||
41 | |||
42 | #endif /* _TRACE_TEST_H || TRACE_HEADER_MULTI_READ */ | ||
43 | |||
44 | #undef TRACE_INCLUDE_PATH | ||
45 | #undef TRACE_INCLUDE_FILE | ||
46 | #define TRACE_INCLUDE_PATH . | ||
47 | #define TRACE_INCLUDE_FILE trace_events_filter_test | ||
48 | |||
49 | /* This part must be outside protection */ | ||
50 | #include <trace/define_trace.h> | ||