From: "Steven Rostedt (VMware)" <rostedt@xxxxxxxxxxx> All for full "regex(3)" processing of setting functions in the set_ftrace_filter file. Check if the filter passed in is just a glob expression that the kernel can process, or if it is a regex that should look at the available_filter_functions list instead. If it is a regex, it will read the available_filter_functions and write in each function as it finds it. Link: https://lore.kernel.org/linux-trace-devel/20210323013225.451281989@xxxxxxxxxxx Signed-off-by: Steven Rostedt (VMware) <rostedt@xxxxxxxxxxx> --- Documentation/libtracefs-function-filter.txt | 10 ++ src/tracefs-tools.c | 139 ++++++++++++++++--- 2 files changed, 128 insertions(+), 21 deletions(-) diff --git a/Documentation/libtracefs-function-filter.txt b/Documentation/libtracefs-function-filter.txt index c0c89f372c21..88aa3b923d54 100644 --- a/Documentation/libtracefs-function-filter.txt +++ b/Documentation/libtracefs-function-filter.txt @@ -32,6 +32,16 @@ _errs_, is a pointer to an array of strings, which will be allocated if any of filters fail to match any available function, If _errs_ is NULL, it will be ignored. +A filter in the array of _filters_ may be either a straight match of a +function, a glob or regex(3). a glob is where '*' matches zero or more +characters, '?' will match zero or one character, and '.' only matches a +period. If the filter is determined to be a regex (where it contains +anything other than alpha numeric characters, or '.', '*', '?') the filter +will be processed as a regex(3) following the rules of regex(3), and '.' is +not a period, but will match any one character. To force a regular +expression, either prefix the filter with a '^' or append it with a '$' as +all filters will act as complete matches of functions anyway. + returns 0 on success, 1 or -x (where x is an integer) on error. RETURN VALUE diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c index 470502b07f7d..d1a448459c6f 100644 --- a/src/tracefs-tools.c +++ b/src/tracefs-tools.c @@ -18,8 +18,9 @@ #include "tracefs.h" #include "tracefs-local.h" -#define TRACE_CTRL "tracing_on" -#define TRACE_FILTER "set_ftrace_filter" +#define TRACE_CTRL "tracing_on" +#define TRACE_FILTER "set_ftrace_filter" +#define TRACE_FILTER_LIST "available_filter_functions" static const char * const options_map[] = { "unknown", @@ -421,8 +422,53 @@ struct func_filter { const char *filter; regex_t re; bool set; + bool is_regex; }; +static bool is_regex(const char *str) +{ + int i; + + for (i = 0; str[i]; i++) { + switch (str[i]) { + case 'a' ... 'z': + case 'A'...'Z': + case '_': + case '0'...'9': + case '*': + case '.': + /* Dots can be part of a function name */ + case '?': + continue; + default: + return true; + } + } + return false; +} + +static char *update_regex(const char *reg) +{ + int len = strlen(reg); + char *str; + + if (reg[0] == '^' && reg[len - 1] == '$') + return strdup(reg); + + str = malloc(len + 3); + if (reg[0] == '^') { + strcpy(str, reg); + } else { + str[0] = '^'; + strcpy(str + 1, reg); + len++; /* add ^ */ + } + if (str[len - 1] != '$') + str[len++]= '$'; + str[len] = '\0'; + return str; +} + /* * Convert a glob into a regular expression. */ @@ -488,8 +534,13 @@ static int write_filter(int fd, const char *filter, const char *module) return 0; } -static int check_available_filters(struct func_filter *func_filters, - const char *module, const char ***errs) +enum match_type { + FILTER_CHECK, + FILTER_WRITE, +}; + +static int match_filters(int fd, struct func_filter *func_filters, + const char *module, enum match_type type) { char *line = NULL; size_t size = 0; @@ -499,7 +550,7 @@ static int check_available_filters(struct func_filter *func_filters, int mlen; int i; - path = tracefs_get_tracing_file("available_filter_functions"); + path = tracefs_get_tracing_file(TRACE_FILTER_LIST); if (!path) return 1; @@ -530,39 +581,76 @@ static int check_available_filters(struct func_filter *func_filters, (mtok[mlen + 1] != ']')) goto next; } - for (i = 0; func_filters[i].filter; i++) { - if (match(tok, &func_filters[i])) - func_filters[i].set = true; + switch (type) { + case FILTER_CHECK: + /* Check, checks a list of filters */ + for (i = 0; func_filters[i].filter; i++) { + if (match(tok, &func_filters[i])) + func_filters[i].set = true; + } + break; + case FILTER_WRITE: + /* Writes only have one filter */ + if (match(tok, func_filters)) { + ret = write_filter(fd, tok, module); + if (ret) + goto out; + } + break; } next: free(line); line = NULL; len = 0; } + out: + free(line); fclose(fp); + return ret; +} + +static int check_available_filters(struct func_filter *func_filters, + const char *module, const char ***errs) +{ + int ret; + int i; + + ret = match_filters(-1, func_filters, module, FILTER_CHECK); + /* Return here if success or non filter error */ + if (ret >= 0) + return ret; + + /* Failed on filter, set the errors */ ret = 0; for (i = 0; func_filters[i].filter; i++) { if (!func_filters[i].set) add_errors(errs, func_filters[i].filter, ret--); } - return ret; } -static int controlled_write(int fd, const char **filters, +static int set_regex_filter(int fd, struct func_filter *func_filter, + const char *module) +{ + return match_filters(fd, func_filter, module, FILTER_WRITE); +} + +static int controlled_write(int fd, struct func_filter *func_filters, const char *module, const char ***errs) { int ret = 0; int i; - for (i = 0; filters[i]; i++) { + for (i = 0; func_filters[i].filter; i++) { + const char *filter = func_filters[i].filter; int r; - r = write_filter(fd, filters[i], module); - if (r < 0) { - add_errors(errs, filters[i], ret--); - } else if (r > 0) { + if (func_filters[i].is_regex) + r = set_regex_filter(fd, &func_filters[i], module); + else + r = write_filter(fd, filter, module); + if (r > 0) { /* Not filter error */ if (errs) { free(*errs); @@ -570,6 +658,8 @@ static int controlled_write(int fd, const char **filters, } return 1; } + if (r < 0) + add_errors(errs, filter, ret--); } return ret; } @@ -579,7 +669,11 @@ static int init_func_filter(struct func_filter *func_filter, const char *filter) char *str; int ret; - str = make_regex(filter); + if (!(func_filter->is_regex = is_regex(filter))) + str = make_regex(filter); + else + str = update_regex(filter); + if (!str) return -1; @@ -679,24 +773,27 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char **filt *errs = NULL; ret = check_available_filters(func_filters, module, errs); - free_func_filters(func_filters); if (ret) - return ret; + goto out_free; + ret = 1; ftrace_filter_path = tracefs_instance_get_file(instance, TRACE_FILTER); if (!ftrace_filter_path) - return 1; + goto out_free; flags = reset ? O_TRUNC : O_APPEND; fd = open(ftrace_filter_path, O_WRONLY | flags); tracefs_put_tracing_file(ftrace_filter_path); if (fd < 0) - return 1; + goto out_free; - ret = controlled_write(fd, filters, module, errs); + ret = controlled_write(fd, func_filters, module, errs); close(fd); + out_free: + free_func_filters(func_filters); + return ret; } -- 2.30.1