ftrace: add command interface for function selection
authorSteven Rostedt <srostedt@redhat.com>
Sat, 14 Feb 2009 05:40:25 +0000 (00:40 -0500)
committerSteven Rostedt <srostedt@redhat.com>
Mon, 16 Feb 2009 22:06:02 +0000 (17:06 -0500)
Allow for other tracers to add their own commands for function
selection. This interface gives a trace the ability to name a
command for function selection. Right now it is pretty limited
in what it offers, but this is a building step for more features.

The :mod: command is converted to this interface and also serves
as a template for other implementations.

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

index 106b7909d500e3fd88118e0f8182cd4bd00d27f3..f0a0ecc63b5c9a69e2541cb02249ea93cec11c2e 100644 (file)
@@ -95,6 +95,13 @@ stack_trace_sysctl(struct ctl_table *table, int write,
                   loff_t *ppos);
 #endif
 
+struct ftrace_func_command {
+       struct list_head        list;
+       char                    *name;
+       int                     (*func)(char *func, char *cmd,
+                                       char *params, int enable);
+};
+
 #ifdef CONFIG_DYNAMIC_FTRACE
 /* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
 #include <asm/ftrace.h>
@@ -119,6 +126,9 @@ struct dyn_ftrace {
 int ftrace_force_update(void);
 void ftrace_set_filter(unsigned char *buf, int len, int reset);
 
+int register_ftrace_command(struct ftrace_func_command *cmd);
+int unregister_ftrace_command(struct ftrace_func_command *cmd);
+
 /* defined in arch */
 extern int ftrace_ip_converted(unsigned long ip);
 extern int ftrace_dyn_arch_init(void *data);
@@ -202,6 +212,12 @@ extern void ftrace_enable_daemon(void);
 # define ftrace_disable_daemon()               do { } while (0)
 # define ftrace_enable_daemon()                        do { } while (0)
 static inline void ftrace_release(void *start, unsigned long size) { }
+static inline int register_ftrace_command(struct ftrace_func_command *cmd)
+{
+}
+static inline int unregister_ftrace_command(char *cmd_name)
+{
+}
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
 /* totally disable ftrace - can not re-enable after this */
index 340f88b68d9ef07b2c392c44c98ef7df7b488745..45a44c4025665bed6b5646c28bcbb0df05121a41 100644 (file)
@@ -1239,9 +1239,93 @@ static void ftrace_match_module_records(char *buff, char *mod, int enable)
        spin_unlock(&ftrace_lock);
 }
 
+/*
+ * We register the module command as a template to show others how
+ * to register the a command as well.
+ */
+
+static int
+ftrace_mod_callback(char *func, char *cmd, char *param, int enable)
+{
+       char *mod;
+
+       /*
+        * cmd == 'mod' because we only registered this func
+        * for the 'mod' ftrace_func_command.
+        * But if you register one func with multiple commands,
+        * you can tell which command was used by the cmd
+        * parameter.
+        */
+
+       /* we must have a module name */
+       if (!param)
+               return -EINVAL;
+
+       mod = strsep(&param, ":");
+       if (!strlen(mod))
+               return -EINVAL;
+
+       ftrace_match_module_records(func, mod, enable);
+       return 0;
+}
+
+static struct ftrace_func_command ftrace_mod_cmd = {
+       .name                   = "mod",
+       .func                   = ftrace_mod_callback,
+};
+
+static int __init ftrace_mod_cmd_init(void)
+{
+       return register_ftrace_command(&ftrace_mod_cmd);
+}
+device_initcall(ftrace_mod_cmd_init);
+
+static LIST_HEAD(ftrace_commands);
+static DEFINE_MUTEX(ftrace_cmd_mutex);
+
+int register_ftrace_command(struct ftrace_func_command *cmd)
+{
+       struct ftrace_func_command *p;
+       int ret = 0;
+
+       mutex_lock(&ftrace_cmd_mutex);
+       list_for_each_entry(p, &ftrace_commands, list) {
+               if (strcmp(cmd->name, p->name) == 0) {
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+       }
+       list_add(&cmd->list, &ftrace_commands);
+ out_unlock:
+       mutex_unlock(&ftrace_cmd_mutex);
+
+       return ret;
+}
+
+int unregister_ftrace_command(struct ftrace_func_command *cmd)
+{
+       struct ftrace_func_command *p, *n;
+       int ret = -ENODEV;
+
+       mutex_lock(&ftrace_cmd_mutex);
+       list_for_each_entry_safe(p, n, &ftrace_commands, list) {
+               if (strcmp(cmd->name, p->name) == 0) {
+                       ret = 0;
+                       list_del_init(&p->list);
+                       goto out_unlock;
+               }
+       }
+ out_unlock:
+       mutex_unlock(&ftrace_cmd_mutex);
+
+       return ret;
+}
+
 static int ftrace_process_regex(char *buff, int len, int enable)
 {
-       char *func, *mod, *command, *next = buff;
+       struct ftrace_func_command *p;
+       char *func, *command, *next = buff;
+       int ret = -EINVAL;
 
        func = strsep(&next, ":");
 
@@ -1250,21 +1334,21 @@ static int ftrace_process_regex(char *buff, int len, int enable)
                return 0;
        }
 
-       /* command fonud */
+       /* command found */
 
        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;
+       mutex_lock(&ftrace_cmd_mutex);
+       list_for_each_entry(p, &ftrace_commands, list) {
+               if (strcmp(p->name, command) == 0) {
+                       ret = p->func(func, command, next, enable);
+                       goto out_unlock;
+               }
        }
+ out_unlock:
+       mutex_unlock(&ftrace_cmd_mutex);
 
-       return -EINVAL;
+       return ret;
 }
 
 static ssize_t