[PATCH 4/5] ftrace: add module globbing

From: Dmitry Safonov
Date: Tue Sep 29 2015 - 12:47:45 EST


Extend module command for function filter selection with globbing.
It uses the same globbing as function filter.

sh# echo '*alloc*:mod:*' > set_ftrace_filter

Will trace any function with the letters 'alloc' in the name in any
module but not in kernel.

sh# echo '!*alloc*:mod:ipv6' >> set_ftrace_filter

Will prevent from tracing functions with 'alloc' in the name from module
ipv6 (do not forget to append to set_ftrace_filter file).

sh# echo '*alloc*:mod:!ipv6' > set_ftrace_filter

Will trace functions with 'alloc' in the name from kernel and any
module except ipv6.

sh# echo '*alloc*:mod:!*' > set_ftrace_filter

Will trace any function with the letters 'alloc' in the name only from
kernel, but not from any module.

sh# echo '*:mod:!*' > set_ftrace_filter
or
sh# echo ':mod:!' > set_ftrace_filter

Will trace every function in the kernel, but will not trace functions
from any module.

sh# echo '*:mod:*' > set_ftrace_filter
or
sh# echo ':mod:' > set_ftrace_filter

As the opposite will trace all functions from all modules, but not from
kernel.

sh# echo '*:mod:*snd*' > set_ftrace_filter

Will trace your sound drivers only (if any).

Signed-off-by: Dmitry Safonov <0x7f454c46@xxxxxxxxx>
---
kernel/trace/ftrace.c | 54 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 17 deletions(-)

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6ffc1a2..a2a4e57 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -3480,19 +3480,37 @@ enter_record(struct ftrace_hash *hash, struct dyn_ftrace *rec, int clear_filter)
}

static int
-ftrace_match_record(struct dyn_ftrace *rec,
- char *mod, struct ftrace_glob *func_g)
+ftrace_match_record(struct dyn_ftrace *rec, struct ftrace_glob *func_g,
+ struct ftrace_glob *mod_g, int exclude_mod)
{
char str[KSYM_SYMBOL_LEN];
char *modname;

kallsyms_lookup(rec->ip, NULL, NULL, &modname, str);

- if (mod) {
- /* module lookup requires matching the module */
- if (!modname || strcmp(modname, mod))
+ if (mod_g) {
+ int mod_matches = (modname) ? ftrace_match(modname, mod_g) : 0;
+
+ /* blank module name to match all modules */
+ if (!mod_g->len) {
+ /* blank module globbing: modname xor exclude_mod */
+ if ((!exclude_mod) != (!modname))
+ goto func_match;
+ return 0;
+ }
+
+ /* not matching the module */
+ if (!modname || !mod_matches) {
+ if (exclude_mod)
+ goto func_match;
+ else
+ return 0;
+ }
+
+ if (mod_matches && exclude_mod)
return 0;

+func_match:
/* blank search means to match all funcs in the mod */
if (!func_g->len)
return 1;
@@ -3507,23 +3525,31 @@ match_records(struct ftrace_hash *hash, char *func, int len, char *mod)
struct ftrace_page *pg;
struct dyn_ftrace *rec;
struct ftrace_glob func_g = { .type = MATCH_FULL };
+ struct ftrace_glob mod_g = { .type = MATCH_FULL };
+ struct ftrace_glob *mod_match = (mod) ? &mod_g : NULL;
int found = 0;
int ret;
- int clear_filter;
+ int clear_filter, exclude_mod = 0;

- if (len) {
+ if (func) {
func_g.type = filter_parse_regex(func, len,
&func_g.search, &clear_filter);
func_g.len = strlen(func_g.search);
}

+ if (mod) {
+ mod_g.type = filter_parse_regex(mod, strlen(mod),
+ &mod_g.search, &exclude_mod);
+ mod_g.len = strlen(mod_g.search);
+ }
+
mutex_lock(&ftrace_lock);

if (unlikely(ftrace_disabled))
goto out_unlock;

do_for_each_ftrace_rec(pg, rec) {
- if (ftrace_match_record(rec, mod, &func_g)) {
+ if (ftrace_match_record(rec, &func_g, mod_match, exclude_mod)) {
ret = enter_record(hash, rec, clear_filter);
if (ret < 0) {
found = ret;
@@ -3554,8 +3580,6 @@ static int
ftrace_mod_callback(struct ftrace_hash *hash,
char *func, char *cmd, char *module, int enable)
{
- int ret;
-
/*
* cmd == 'mod' because we only registered this func
* for the 'mod' ftrace_func_command.
@@ -3564,16 +3588,12 @@ ftrace_mod_callback(struct ftrace_hash *hash,
* parameter.
*/

- /* we must have a module name */
- if (!module || !strlen(module))
- return -EINVAL;
+ int ret = match_records(hash, func, strlen(func), module);

- ret = match_records(hash, func, strlen(func), module);
if (ret == 0)
return -EINVAL;
if (ret < 0)
return ret;
-
return 0;
}

@@ -3724,7 +3744,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,

do_for_each_ftrace_rec(pg, rec) {

- if (!ftrace_match_record(rec, NULL, &func_g))
+ if (!ftrace_match_record(rec, &func_g, NULL, 0))
continue;

entry = kmalloc(sizeof(*entry), GFP_KERNEL);
@@ -4616,7 +4636,7 @@ ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer)

do_for_each_ftrace_rec(pg, rec) {

- if (ftrace_match_record(rec, NULL, &func_g)) {
+ if (ftrace_match_record(rec, &func_g, NULL, 0)) {
/* if it is in the array */
exists = false;
for (i = 0; i < *idx; i++) {
--
2.5.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/