diff options
Diffstat (limited to 'kernel/trace')
| -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> | ||
