diff options
-rw-r--r-- | include/linux/ftrace.h | 7 | ||||
-rw-r--r-- | kernel/trace/ftrace.c | 6 | ||||
-rw-r--r-- | kernel/trace/trace.h | 2 | ||||
-rw-r--r-- | kernel/trace/trace_event_perf.c | 4 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 165 |
5 files changed, 172 insertions, 12 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 64a309d2fbd6..72a6cabb4d5b 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -250,6 +250,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, | |||
250 | int len, int reset); | 250 | int len, int reset); |
251 | void ftrace_set_global_filter(unsigned char *buf, int len, int reset); | 251 | void ftrace_set_global_filter(unsigned char *buf, int len, int reset); |
252 | void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); | 252 | void ftrace_set_global_notrace(unsigned char *buf, int len, int reset); |
253 | void ftrace_free_filter(struct ftrace_ops *ops); | ||
253 | 254 | ||
254 | int register_ftrace_command(struct ftrace_func_command *cmd); | 255 | int register_ftrace_command(struct ftrace_func_command *cmd); |
255 | int unregister_ftrace_command(struct ftrace_func_command *cmd); | 256 | int unregister_ftrace_command(struct ftrace_func_command *cmd); |
@@ -380,9 +381,6 @@ extern void ftrace_enable_daemon(void); | |||
380 | #else | 381 | #else |
381 | static inline int skip_trace(unsigned long ip) { return 0; } | 382 | static inline int skip_trace(unsigned long ip) { return 0; } |
382 | static inline int ftrace_force_update(void) { return 0; } | 383 | static inline int ftrace_force_update(void) { return 0; } |
383 | static inline void ftrace_set_filter(unsigned char *buf, int len, int reset) | ||
384 | { | ||
385 | } | ||
386 | static inline void ftrace_disable_daemon(void) { } | 384 | static inline void ftrace_disable_daemon(void) { } |
387 | static inline void ftrace_enable_daemon(void) { } | 385 | static inline void ftrace_enable_daemon(void) { } |
388 | static inline void ftrace_release_mod(struct module *mod) {} | 386 | static inline void ftrace_release_mod(struct module *mod) {} |
@@ -406,6 +404,9 @@ static inline int ftrace_text_reserved(void *start, void *end) | |||
406 | */ | 404 | */ |
407 | #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) | 405 | #define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; }) |
408 | #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) | 406 | #define ftrace_set_early_filter(ops, buf, enable) do { } while (0) |
407 | #define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; }) | ||
408 | #define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; }) | ||
409 | #define ftrace_free_filter(ops) do { } while (0) | ||
409 | 410 | ||
410 | static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, | 411 | static inline ssize_t ftrace_filter_write(struct file *file, const char __user *ubuf, |
411 | size_t cnt, loff_t *ppos) { return -ENODEV; } | 412 | size_t cnt, loff_t *ppos) { return -ENODEV; } |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f615f974d90e..867bd1dd2dd0 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -1186,6 +1186,12 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) | |||
1186 | call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); | 1186 | call_rcu_sched(&hash->rcu, __free_ftrace_hash_rcu); |
1187 | } | 1187 | } |
1188 | 1188 | ||
1189 | void ftrace_free_filter(struct ftrace_ops *ops) | ||
1190 | { | ||
1191 | free_ftrace_hash(ops->filter_hash); | ||
1192 | free_ftrace_hash(ops->notrace_hash); | ||
1193 | } | ||
1194 | |||
1189 | static struct ftrace_hash *alloc_ftrace_hash(int size_bits) | 1195 | static struct ftrace_hash *alloc_ftrace_hash(int size_bits) |
1190 | { | 1196 | { |
1191 | struct ftrace_hash *hash; | 1197 | struct ftrace_hash *hash; |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 29f93cd434a5..54faec790bc1 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -776,9 +776,7 @@ struct filter_pred { | |||
776 | u64 val; | 776 | u64 val; |
777 | struct regex regex; | 777 | struct regex regex; |
778 | unsigned short *ops; | 778 | unsigned short *ops; |
779 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
780 | struct ftrace_event_field *field; | 779 | struct ftrace_event_field *field; |
781 | #endif | ||
782 | int offset; | 780 | int offset; |
783 | int not; | 781 | int not; |
784 | int op; | 782 | int op; |
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index fdeeb5c49627..fee3752ae8f6 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c | |||
@@ -298,7 +298,9 @@ static int perf_ftrace_function_register(struct perf_event *event) | |||
298 | static int perf_ftrace_function_unregister(struct perf_event *event) | 298 | static int perf_ftrace_function_unregister(struct perf_event *event) |
299 | { | 299 | { |
300 | struct ftrace_ops *ops = &event->ftrace_ops; | 300 | struct ftrace_ops *ops = &event->ftrace_ops; |
301 | return unregister_ftrace_function(ops); | 301 | int ret = unregister_ftrace_function(ops); |
302 | ftrace_free_filter(ops); | ||
303 | return ret; | ||
302 | } | 304 | } |
303 | 305 | ||
304 | static void perf_ftrace_function_enable(struct perf_event *event) | 306 | static void perf_ftrace_function_enable(struct perf_event *event) |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 3da3d0ec3584..431dba8b7542 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -81,6 +81,7 @@ enum { | |||
81 | FILT_ERR_TOO_MANY_PREDS, | 81 | FILT_ERR_TOO_MANY_PREDS, |
82 | FILT_ERR_MISSING_FIELD, | 82 | FILT_ERR_MISSING_FIELD, |
83 | FILT_ERR_INVALID_FILTER, | 83 | FILT_ERR_INVALID_FILTER, |
84 | FILT_ERR_IP_FIELD_ONLY, | ||
84 | }; | 85 | }; |
85 | 86 | ||
86 | static char *err_text[] = { | 87 | static char *err_text[] = { |
@@ -96,6 +97,7 @@ static char *err_text[] = { | |||
96 | "Too many terms in predicate expression", | 97 | "Too many terms in predicate expression", |
97 | "Missing field name and/or value", | 98 | "Missing field name and/or value", |
98 | "Meaningless filter expression", | 99 | "Meaningless filter expression", |
100 | "Only 'ip' field is supported for function trace", | ||
99 | }; | 101 | }; |
100 | 102 | ||
101 | struct opstack_op { | 103 | struct opstack_op { |
@@ -991,7 +993,12 @@ static int init_pred(struct filter_parse_state *ps, | |||
991 | fn = filter_pred_strloc; | 993 | fn = filter_pred_strloc; |
992 | else | 994 | else |
993 | fn = filter_pred_pchar; | 995 | fn = filter_pred_pchar; |
994 | } else if (!is_function_field(field)) { | 996 | } else if (is_function_field(field)) { |
997 | if (strcmp(field->name, "ip")) { | ||
998 | parse_error(ps, FILT_ERR_IP_FIELD_ONLY, 0); | ||
999 | return -EINVAL; | ||
1000 | } | ||
1001 | } else { | ||
995 | if (field->is_signed) | 1002 | if (field->is_signed) |
996 | ret = strict_strtoll(pred->regex.pattern, 0, &val); | 1003 | ret = strict_strtoll(pred->regex.pattern, 0, &val); |
997 | else | 1004 | else |
@@ -1338,10 +1345,7 @@ static struct filter_pred *create_pred(struct filter_parse_state *ps, | |||
1338 | 1345 | ||
1339 | strcpy(pred.regex.pattern, operand2); | 1346 | strcpy(pred.regex.pattern, operand2); |
1340 | pred.regex.len = strlen(pred.regex.pattern); | 1347 | pred.regex.len = strlen(pred.regex.pattern); |
1341 | |||
1342 | #ifdef CONFIG_FTRACE_STARTUP_TEST | ||
1343 | pred.field = field; | 1348 | pred.field = field; |
1344 | #endif | ||
1345 | return init_pred(ps, field, &pred) ? NULL : &pred; | 1349 | return init_pred(ps, field, &pred) ? NULL : &pred; |
1346 | } | 1350 | } |
1347 | 1351 | ||
@@ -1954,6 +1958,148 @@ void ftrace_profile_free_filter(struct perf_event *event) | |||
1954 | __free_filter(filter); | 1958 | __free_filter(filter); |
1955 | } | 1959 | } |
1956 | 1960 | ||
1961 | struct function_filter_data { | ||
1962 | struct ftrace_ops *ops; | ||
1963 | int first_filter; | ||
1964 | int first_notrace; | ||
1965 | }; | ||
1966 | |||
1967 | #ifdef CONFIG_FUNCTION_TRACER | ||
1968 | static char ** | ||
1969 | ftrace_function_filter_re(char *buf, int len, int *count) | ||
1970 | { | ||
1971 | char *str, *sep, **re; | ||
1972 | |||
1973 | str = kstrndup(buf, len, GFP_KERNEL); | ||
1974 | if (!str) | ||
1975 | return NULL; | ||
1976 | |||
1977 | /* | ||
1978 | * The argv_split function takes white space | ||
1979 | * as a separator, so convert ',' into spaces. | ||
1980 | */ | ||
1981 | while ((sep = strchr(str, ','))) | ||
1982 | *sep = ' '; | ||
1983 | |||
1984 | re = argv_split(GFP_KERNEL, str, count); | ||
1985 | kfree(str); | ||
1986 | return re; | ||
1987 | } | ||
1988 | |||
1989 | static int ftrace_function_set_regexp(struct ftrace_ops *ops, int filter, | ||
1990 | int reset, char *re, int len) | ||
1991 | { | ||
1992 | int ret; | ||
1993 | |||
1994 | if (filter) | ||
1995 | ret = ftrace_set_filter(ops, re, len, reset); | ||
1996 | else | ||
1997 | ret = ftrace_set_notrace(ops, re, len, reset); | ||
1998 | |||
1999 | return ret; | ||
2000 | } | ||
2001 | |||
2002 | static int __ftrace_function_set_filter(int filter, char *buf, int len, | ||
2003 | struct function_filter_data *data) | ||
2004 | { | ||
2005 | int i, re_cnt, ret; | ||
2006 | int *reset; | ||
2007 | char **re; | ||
2008 | |||
2009 | reset = filter ? &data->first_filter : &data->first_notrace; | ||
2010 | |||
2011 | /* | ||
2012 | * The 'ip' field could have multiple filters set, separated | ||
2013 | * either by space or comma. We first cut the filter and apply | ||
2014 | * all pieces separatelly. | ||
2015 | */ | ||
2016 | re = ftrace_function_filter_re(buf, len, &re_cnt); | ||
2017 | if (!re) | ||
2018 | return -EINVAL; | ||
2019 | |||
2020 | for (i = 0; i < re_cnt; i++) { | ||
2021 | ret = ftrace_function_set_regexp(data->ops, filter, *reset, | ||
2022 | re[i], strlen(re[i])); | ||
2023 | if (ret) | ||
2024 | break; | ||
2025 | |||
2026 | if (*reset) | ||
2027 | *reset = 0; | ||
2028 | } | ||
2029 | |||
2030 | argv_free(re); | ||
2031 | return ret; | ||
2032 | } | ||
2033 | |||
2034 | static int ftrace_function_check_pred(struct filter_pred *pred, int leaf) | ||
2035 | { | ||
2036 | struct ftrace_event_field *field = pred->field; | ||
2037 | |||
2038 | if (leaf) { | ||
2039 | /* | ||
2040 | * Check the leaf predicate for function trace, verify: | ||
2041 | * - only '==' and '!=' is used | ||
2042 | * - the 'ip' field is used | ||
2043 | */ | ||
2044 | if ((pred->op != OP_EQ) && (pred->op != OP_NE)) | ||
2045 | return -EINVAL; | ||
2046 | |||
2047 | if (strcmp(field->name, "ip")) | ||
2048 | return -EINVAL; | ||
2049 | } else { | ||
2050 | /* | ||
2051 | * Check the non leaf predicate for function trace, verify: | ||
2052 | * - only '||' is used | ||
2053 | */ | ||
2054 | if (pred->op != OP_OR) | ||
2055 | return -EINVAL; | ||
2056 | } | ||
2057 | |||
2058 | return 0; | ||
2059 | } | ||
2060 | |||
2061 | static int ftrace_function_set_filter_cb(enum move_type move, | ||
2062 | struct filter_pred *pred, | ||
2063 | int *err, void *data) | ||
2064 | { | ||
2065 | /* Checking the node is valid for function trace. */ | ||
2066 | if ((move != MOVE_DOWN) || | ||
2067 | (pred->left != FILTER_PRED_INVALID)) { | ||
2068 | *err = ftrace_function_check_pred(pred, 0); | ||
2069 | } else { | ||
2070 | *err = ftrace_function_check_pred(pred, 1); | ||
2071 | if (*err) | ||
2072 | return WALK_PRED_ABORT; | ||
2073 | |||
2074 | *err = __ftrace_function_set_filter(pred->op == OP_EQ, | ||
2075 | pred->regex.pattern, | ||
2076 | pred->regex.len, | ||
2077 | data); | ||
2078 | } | ||
2079 | |||
2080 | return (*err) ? WALK_PRED_ABORT : WALK_PRED_DEFAULT; | ||
2081 | } | ||
2082 | |||
2083 | static int ftrace_function_set_filter(struct perf_event *event, | ||
2084 | struct event_filter *filter) | ||
2085 | { | ||
2086 | struct function_filter_data data = { | ||
2087 | .first_filter = 1, | ||
2088 | .first_notrace = 1, | ||
2089 | .ops = &event->ftrace_ops, | ||
2090 | }; | ||
2091 | |||
2092 | return walk_pred_tree(filter->preds, filter->root, | ||
2093 | ftrace_function_set_filter_cb, &data); | ||
2094 | } | ||
2095 | #else | ||
2096 | static int ftrace_function_set_filter(struct perf_event *event, | ||
2097 | struct event_filter *filter) | ||
2098 | { | ||
2099 | return -ENODEV; | ||
2100 | } | ||
2101 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
2102 | |||
1957 | int ftrace_profile_set_filter(struct perf_event *event, int event_id, | 2103 | int ftrace_profile_set_filter(struct perf_event *event, int event_id, |
1958 | char *filter_str) | 2104 | char *filter_str) |
1959 | { | 2105 | { |
@@ -1974,9 +2120,16 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id, | |||
1974 | goto out_unlock; | 2120 | goto out_unlock; |
1975 | 2121 | ||
1976 | err = create_filter(call, filter_str, false, &filter); | 2122 | err = create_filter(call, filter_str, false, &filter); |
1977 | if (!err) | 2123 | if (err) |
1978 | event->filter = filter; | 2124 | goto free_filter; |
2125 | |||
2126 | if (ftrace_event_is_function(call)) | ||
2127 | err = ftrace_function_set_filter(event, filter); | ||
1979 | else | 2128 | else |
2129 | event->filter = filter; | ||
2130 | |||
2131 | free_filter: | ||
2132 | if (err || ftrace_event_is_function(call)) | ||
1980 | __free_filter(filter); | 2133 | __free_filter(filter); |
1981 | 2134 | ||
1982 | out_unlock: | 2135 | out_unlock: |