ftrace: add module command function filter selection
authorSteven Rostedt <srostedt@redhat.com>
Fri, 13 Feb 2009 22:08:48 +0000 (17:08 -0500)
committerSteven Rostedt <srostedt@redhat.com>
Mon, 16 Feb 2009 21:55:50 +0000 (16:55 -0500)
This patch adds a "command" syntax to the function filtering files:

  /debugfs/tracing/set_ftrace_filter
  /debugfs/tracing/set_ftrace_notrace

Of the format:  <function>:<command>:<parameter>

The command is optional, and dependent on the command, so are
the parameters.

 echo do_fork > set_ftrace_filter

Will only trace 'do_fork'.

 echo 'sched_*' > set_ftrace_filter

Will only trace functions starting with the letters 'sched_'.

 echo '*:mod:ext3' > set_ftrace_filter

Will trace only the ext3 module functions.

 echo '*write*:mod:ext3' > set_ftrace_notrace

Will prevent the ext3 functions with the letters 'write' in
the name from being traced.

 echo '!*_allocate:mod:ext3' > set_ftrace_filter

Will remove the functions in ext3 that end with the letters
'_allocate' from the ftrace filter.

Although this patch implements the 'command' format, only the
'mod' command is supported. More commands to follow.

Signed-off-by: Steven Rostedt <srostedt@redhat.com>
kernel/trace/ftrace.c

index fcec31323a10cd668143b1e1d9dbafec27d2adf6..9e60ae423af9d27068d027cfbbe63ad1721fef54 100644 (file)
@@ -1067,7 +1067,7 @@ enum {
  *     0 otherwise.
  */
 static int
-ftrace_setup_glob(unsigned char *buff, int len, char **search, int *not)
+ftrace_setup_glob(char *buff, int len, char **search, int *not)
 {
        int type = MATCH_FULL;
        int i;
@@ -1100,14 +1100,11 @@ ftrace_setup_glob(unsigned char *buff, int len, char **search, int *not)
        return type;
 }
 
-static int
-ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
+static int ftrace_match(char *str, char *regex, int len, int type)
 {
-       char str[KSYM_SYMBOL_LEN];
        int matched = 0;
        char *ptr;
 
-       kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
        switch (type) {
        case MATCH_FULL:
                if (strcmp(str, regex) == 0)
@@ -1131,6 +1128,15 @@ ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
        return matched;
 }
 
+static int
+ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
+{
+       char str[KSYM_SYMBOL_LEN];
+
+       kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
+       return ftrace_match(str, regex, len, type);
+}
+
 static void ftrace_match_records(char *buff, int len, int enable)
 {
        char *search;
@@ -1165,6 +1171,100 @@ static void ftrace_match_records(char *buff, int len, int enable)
        spin_unlock(&ftrace_lock);
 }
 
+static int
+ftrace_match_module_record(struct dyn_ftrace *rec, char *mod,
+                          char *regex, int len, int type)
+{
+       char str[KSYM_SYMBOL_LEN];
+       char *modname;
+
+       kallsyms_lookup(rec->ip, NULL, NULL, &modname, str);
+
+       if (!modname || strcmp(modname, mod))
+               return 0;
+
+       /* blank search means to match all funcs in the mod */
+       if (len)
+               return ftrace_match(str, regex, len, type);
+       else
+               return 1;
+}
+
+static void ftrace_match_module_records(char *buff, char *mod, int enable)
+{
+       char *search = buff;
+       struct ftrace_page *pg;
+       struct dyn_ftrace *rec;
+       int type = MATCH_FULL;
+       unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
+       unsigned search_len = 0;
+       int not = 0;
+
+       /* blank or '*' mean the same */
+       if (strcmp(buff, "*") == 0)
+               buff[0] = 0;
+
+       /* handle the case of 'dont filter this module' */
+       if (strcmp(buff, "!") == 0 || strcmp(buff, "!*") == 0) {
+               buff[0] = 0;
+               not = 1;
+       }
+
+       if (strlen(buff)) {
+               type = ftrace_setup_glob(buff, strlen(buff), &search, &not);
+               search_len = strlen(search);
+       }
+
+       /* should not be called from interrupt context */
+       spin_lock(&ftrace_lock);
+       if (enable)
+               ftrace_filtered = 1;
+
+       do_for_each_ftrace_rec(pg, rec) {
+
+               if (rec->flags & FTRACE_FL_FAILED)
+                       continue;
+
+               if (ftrace_match_module_record(rec, mod,
+                                              search, search_len, type)) {
+                       if (not)
+                               rec->flags &= ~flag;
+                       else
+                               rec->flags |= flag;
+               }
+
+       } while_for_each_ftrace_rec();
+       spin_unlock(&ftrace_lock);
+}
+
+static int ftrace_process_regex(char *buff, int len, int enable)
+{
+       char *func, *mod, *command, *next = buff;
+
+       func = strsep(&next, ":");
+
+       if (!next) {
+               ftrace_match_records(func, len, enable);
+               return 0;
+       }
+
+       /* command fonud */
+
+       command = strsep(&next, ":");
+
+       if (strcmp(command, "mod") == 0) {
+               /* only match modules */
+               if (!next)
+                       return -EINVAL;
+
+               mod = strsep(&next, ":");
+               ftrace_match_module_records(func, mod, enable);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
 static ssize_t
 ftrace_regex_write(struct file *file, const char __user *ubuf,
                   size_t cnt, loff_t *ppos, int enable)
@@ -1232,7 +1332,10 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
        if (isspace(ch)) {
                iter->filtered++;
                iter->buffer[iter->buffer_idx] = 0;
-               ftrace_match_records(iter->buffer, iter->buffer_idx, enable);
+               ret = ftrace_process_regex(iter->buffer,
+                                          iter->buffer_idx, enable);
+               if (ret)
+                       goto out;
                iter->buffer_idx = 0;
        } else
                iter->flags |= FTRACE_ITER_CONT;