From: "Steven Rostedt (VMware)" <rostedt@xxxxxxxxxxx> Starting in Linux v4.13, it is possible to load filters of a function for a module before it is loaded. That is: # echo '*:mod:rfkill' > set_ftrace_filter # echo function > current_tracer # modprobe rfkill And the above will enable all functions for the rfkill module to be traced before it starts to run. Add a TRACEFS_FL_FUTURE flag to be able to accomplish the same thing. Signed-off-by: Steven Rostedt (VMware) <rostedt@xxxxxxxxxxx> --- Documentation/libtracefs-function-filter.txt | 9 ++++ include/tracefs.h | 3 ++ src/tracefs-tools.c | 53 +++++++++++++++++--- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Documentation/libtracefs-function-filter.txt b/Documentation/libtracefs-function-filter.txt index fa0f0de6f567..ccf20e9671c6 100644 --- a/Documentation/libtracefs-function-filter.txt +++ b/Documentation/libtracefs-function-filter.txt @@ -71,6 +71,15 @@ the *TRACEFS_FL_CONTINUE* flag set for the same instance will fail if *TRACEFS_FL_RESET* flag is set, as the reset flag is only applicable for the first filter to be added before committing. +*TRACEFS_FL_FUTURE* : +If _flags_ contains *TRACEFS_FL_FUTURE* and _module_ holds a string of a module, +then if the module is not loaded it will attemp to write the filter with the module +in the filter file. Starting in Linux v4.13 module functions could be added to the +filter before they are loaded. The filter will be cached, and when the module is +loaded, the filter will be set before the module executes, allowing to trace +init functions of a module. This will only work if the _filter_ is not a +regular expression. + RETURN VALUE ------------ Returns 0 on success. If the there is an error but the filtering was not diff --git a/include/tracefs.h b/include/tracefs.h index 0d98c10ef325..befcc48d265d 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -149,10 +149,13 @@ const char *tracefs_option_name(enum tracefs_option_id id); /* * RESET - Reset on opening filter file (O_TRUNC) * CONTINUE - Do not close filter file on return. + * FUTURE - For kernels that support this feature, enable filters for + * a module that has yet to be loaded. */ enum { TRACEFS_FL_RESET = (1 << 0), TRACEFS_FL_CONTINUE = (1 << 1), + TRACEFS_FL_FUTURE = (1 << 2), }; int tracefs_function_filter(struct tracefs_instance *instance, const char *filter, diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c index 82809fae3e6d..cb07b6fc1e3a 100644 --- a/src/tracefs-tools.c +++ b/src/tracefs-tools.c @@ -553,14 +553,18 @@ static void free_func_list(struct func_list *func_list) } enum match_type { - FILTER_CHECK, - FILTER_WRITE, + FILTER_CHECK = (1 << 0), + FILTER_WRITE = (1 << 1), + FILTER_FUTURE = (1 << 2), }; static int match_filters(int fd, struct func_filter *func_filter, const char *module, struct func_list **func_list, - enum match_type type) + int flags) { + enum match_type type = flags & (FILTER_CHECK | FILTER_WRITE); + bool future = flags & FILTER_FUTURE; + bool mod_match = false; char *line = NULL; size_t size = 0; char *path; @@ -602,6 +606,8 @@ static int match_filters(int fd, struct func_filter *func_filter, if ((strncmp(mtok + 1, module, mlen) != 0) || (mtok[mlen + 1] != ']')) goto next; + if (future) + mod_match = true; } switch (type) { case FILTER_CHECK: @@ -620,6 +626,11 @@ static int match_filters(int fd, struct func_filter *func_filter, goto out; } break; + default: + /* Should never happen */ + ret = -1; + goto out; + } next: free(line); @@ -630,14 +641,21 @@ static int match_filters(int fd, struct func_filter *func_filter, free(line); fclose(fp); + /* If there was no matches and future was set, this is a success */ + if (future && !mod_match) + ret = 0; + return ret; } static int check_available_filters(struct func_filter *func_filter, const char *module, - struct func_list **func_list) + struct func_list **func_list, + bool future) { - return match_filters(-1, func_filter, module, func_list, FILTER_CHECK); + int flags = FILTER_CHECK | (future ? FILTER_FUTURE : 0); + + return match_filters(-1, func_filter, module, func_list, flags); } static int set_regex_filter(int fd, struct func_filter *func_filter, @@ -748,6 +766,13 @@ static int write_func_list(int fd, struct func_list *list) * may be added before they take effect. The last call of this * function must be called without this flag for the filter * to take effect. + * TRACEFS_FL_FUTURE - only applicable if "module" is set. If no match + * is made, and the module is not yet loaded, it will still attempt + * to write the filter plus the module; "<filter>:mod:<module>" + * to the filter file. Starting with Linux kernels 4.13, it is possible + * to load the filter file with module functions for a module that + * is not yet loaded, and when the module is loaded, it will then + * activate the module. * * Returns 0 on success, 1 if there was an error but the filtering has not * yet started, -1 if there was an error but the filtering has started. @@ -763,10 +788,17 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char *filte char *ftrace_filter_path; bool reset = flags & TRACEFS_FL_RESET; bool cont = flags & TRACEFS_FL_CONTINUE; + bool future = flags & TRACEFS_FL_FUTURE; int open_flags; int ret = 1; int *fd; + /* future flag is only applicable to modules */ + if (future && !module) { + errno = EINVAL; + return 1; + } + pthread_mutex_lock(&filter_lock); if (instance) fd = &instance->ftrace_filter_fd; @@ -808,7 +840,7 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char *filte if (init_func_filter(&func_filter, filter) < 0) goto out; - ret = check_available_filters(&func_filter, module, &func_list); + ret = check_available_filters(&func_filter, module, &func_list, future); if (ret) goto out_free; @@ -831,7 +863,14 @@ int tracefs_function_filter(struct tracefs_instance *instance, const char *filte ret = 0; if (filter) { - ret = write_func_list(*fd, func_list); + /* + * If future is set, and no functions were found, then + * set it directly. + */ + if (func_list) + ret = write_func_list(*fd, func_list); + else + ret = 1; if (ret > 0) ret = controlled_write(*fd, &func_filter, module); } -- 2.30.1