ftrace: use code patching for ftrace graph tracer
[pandora-kernel.git] / kernel / trace / ftrace.c
index 53042f1..5f7c864 100644 (file)
@@ -47,6 +47,9 @@
 int ftrace_enabled __read_mostly;
 static int last_ftrace_enabled;
 
+/* ftrace_pid_trace >= 0 will only trace threads with this pid */
+static int ftrace_pid_trace = -1;
+
 /* Quick disabling of function tracer. */
 int function_trace_stop;
 
@@ -61,6 +64,7 @@ static int ftrace_disabled __read_mostly;
 
 static DEFINE_SPINLOCK(ftrace_lock);
 static DEFINE_MUTEX(ftrace_sysctl_lock);
+static DEFINE_MUTEX(ftrace_start_lock);
 
 static struct ftrace_ops ftrace_list_end __read_mostly =
 {
@@ -70,6 +74,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly =
 static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end;
 ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
 ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
+ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
 
 static void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
 {
@@ -86,6 +91,21 @@ static void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
        };
 }
 
+static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip)
+{
+       if (current->pid != ftrace_pid_trace)
+               return;
+
+       ftrace_pid_function(ip, parent_ip);
+}
+
+static void set_ftrace_pid_function(ftrace_func_t func)
+{
+       /* do not set ftrace_pid_function to itself! */
+       if (func != ftrace_pid_func)
+               ftrace_pid_function = func;
+}
+
 /**
  * clear_ftrace_function - reset the ftrace function
  *
@@ -96,6 +116,7 @@ void clear_ftrace_function(void)
 {
        ftrace_trace_function = ftrace_stub;
        __ftrace_trace_function = ftrace_stub;
+       ftrace_pid_function = ftrace_stub;
 }
 
 #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
@@ -128,20 +149,26 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
        ftrace_list = ops;
 
        if (ftrace_enabled) {
+               ftrace_func_t func;
+
+               if (ops->next == &ftrace_list_end)
+                       func = ops->func;
+               else
+                       func = ftrace_list_func;
+
+               if (ftrace_pid_trace >= 0) {
+                       set_ftrace_pid_function(func);
+                       func = ftrace_pid_func;
+               }
+
                /*
                 * For one func, simply call it directly.
                 * For more than one func, call the chain.
                 */
 #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
-               if (ops->next == &ftrace_list_end)
-                       ftrace_trace_function = ops->func;
-               else
-                       ftrace_trace_function = ftrace_list_func;
+               ftrace_trace_function = func;
 #else
-               if (ops->next == &ftrace_list_end)
-                       __ftrace_trace_function = ops->func;
-               else
-                       __ftrace_trace_function = ftrace_list_func;
+               __ftrace_trace_function = func;
                ftrace_trace_function = ftrace_test_stop_func;
 #endif
        }
@@ -182,8 +209,19 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
 
        if (ftrace_enabled) {
                /* If we only have one func left, then call that directly */
-               if (ftrace_list->next == &ftrace_list_end)
-                       ftrace_trace_function = ftrace_list->func;
+               if (ftrace_list->next == &ftrace_list_end) {
+                       ftrace_func_t func = ftrace_list->func;
+
+                       if (ftrace_pid_trace >= 0) {
+                               set_ftrace_pid_function(func);
+                               func = ftrace_pid_func;
+                       }
+#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
+                       ftrace_trace_function = func;
+#else
+                       __ftrace_trace_function = func;
+#endif
+               }
        }
 
  out:
@@ -192,6 +230,38 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
        return ret;
 }
 
+static void ftrace_update_pid_func(void)
+{
+       ftrace_func_t func;
+
+       /* should not be called from interrupt context */
+       spin_lock(&ftrace_lock);
+
+       if (ftrace_trace_function == ftrace_stub)
+               goto out;
+
+       func = ftrace_trace_function;
+
+       if (ftrace_pid_trace >= 0) {
+               set_ftrace_pid_function(func);
+               func = ftrace_pid_func;
+       } else {
+               if (func != ftrace_pid_func)
+                       goto out;
+
+               set_ftrace_pid_function(func);
+       }
+
+#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
+       ftrace_trace_function = func;
+#else
+       __ftrace_trace_function = func;
+#endif
+
+ out:
+       spin_unlock(&ftrace_lock);
+}
+
 #ifdef CONFIG_DYNAMIC_FTRACE
 #ifndef CONFIG_FTRACE_MCOUNT_RECORD
 # error Dynamic ftrace depends on MCOUNT_RECORD
@@ -211,6 +281,8 @@ enum {
        FTRACE_UPDATE_TRACE_FUNC        = (1 << 2),
        FTRACE_ENABLE_MCOUNT            = (1 << 3),
        FTRACE_DISABLE_MCOUNT           = (1 << 4),
+       FTRACE_START_FUNC_RET           = (1 << 5),
+       FTRACE_STOP_FUNC_RET            = (1 << 6),
 };
 
 static int ftrace_filtered;
@@ -395,14 +467,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
        unsigned long ip, fl;
        unsigned long ftrace_addr;
 
-#ifdef CONFIG_FUNCTION_RET_TRACER
-       if (ftrace_tracing_type == FTRACE_TYPE_ENTER)
-               ftrace_addr = (unsigned long)ftrace_caller;
-       else
-               ftrace_addr = (unsigned long)ftrace_return_caller;
-#else
        ftrace_addr = (unsigned long)ftrace_caller;
-#endif
 
        ip = rec->ip;
 
@@ -535,6 +600,11 @@ static int __ftrace_modify_code(void *data)
        if (*command & FTRACE_UPDATE_TRACE_FUNC)
                ftrace_update_ftrace_func(ftrace_trace_function);
 
+       if (*command & FTRACE_START_FUNC_RET)
+               ftrace_enable_ftrace_graph_caller();
+       else if (*command & FTRACE_STOP_FUNC_RET)
+               ftrace_disable_ftrace_graph_caller();
+
        return 0;
 }
 
@@ -545,12 +615,22 @@ static void ftrace_run_update_code(int command)
 
 static ftrace_func_t saved_ftrace_func;
 static int ftrace_start_up;
-static DEFINE_MUTEX(ftrace_start_lock);
 
-static void ftrace_startup(void)
+static void ftrace_startup_enable(int command)
 {
-       int command = 0;
+       if (saved_ftrace_func != ftrace_trace_function) {
+               saved_ftrace_func = ftrace_trace_function;
+               command |= FTRACE_UPDATE_TRACE_FUNC;
+       }
+
+       if (!command || !ftrace_enabled)
+               return;
+
+       ftrace_run_update_code(command);
+}
 
+static void ftrace_startup(int command)
+{
        if (unlikely(ftrace_disabled))
                return;
 
@@ -558,23 +638,13 @@ static void ftrace_startup(void)
        ftrace_start_up++;
        command |= FTRACE_ENABLE_CALLS;
 
-       if (saved_ftrace_func != ftrace_trace_function) {
-               saved_ftrace_func = ftrace_trace_function;
-               command |= FTRACE_UPDATE_TRACE_FUNC;
-       }
-
-       if (!command || !ftrace_enabled)
-               goto out;
+       ftrace_startup_enable(command);
 
-       ftrace_run_update_code(command);
- out:
        mutex_unlock(&ftrace_start_lock);
 }
 
-static void ftrace_shutdown(void)
+static void ftrace_shutdown(int command)
 {
-       int command = 0;
-
        if (unlikely(ftrace_disabled))
                return;
 
@@ -1262,13 +1332,10 @@ static struct file_operations ftrace_notrace_fops = {
        .release = ftrace_notrace_release,
 };
 
-static __init int ftrace_init_debugfs(void)
+static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
 {
-       struct dentry *d_tracer;
        struct dentry *entry;
 
-       d_tracer = tracing_init_dentry();
-
        entry = debugfs_create_file("available_filter_functions", 0444,
                                    d_tracer, NULL, &ftrace_avail_fops);
        if (!entry)
@@ -1295,8 +1362,6 @@ static __init int ftrace_init_debugfs(void)
        return 0;
 }
 
-fs_initcall(ftrace_init_debugfs);
-
 static int ftrace_convert_nops(struct module *mod,
                               unsigned long *start,
                               unsigned long *end)
@@ -1382,12 +1447,101 @@ static int __init ftrace_nodyn_init(void)
 }
 device_initcall(ftrace_nodyn_init);
 
-# define ftrace_startup()              do { } while (0)
-# define ftrace_shutdown()             do { } while (0)
+static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }
+static inline void ftrace_startup_enable(int command) { }
+/* Keep as macros so we do not need to define the commands */
+# define ftrace_startup(command)       do { } while (0)
+# define ftrace_shutdown(command)      do { } while (0)
 # define ftrace_startup_sysctl()       do { } while (0)
 # define ftrace_shutdown_sysctl()      do { } while (0)
 #endif /* CONFIG_DYNAMIC_FTRACE */
 
+static ssize_t
+ftrace_pid_read(struct file *file, char __user *ubuf,
+                      size_t cnt, loff_t *ppos)
+{
+       char buf[64];
+       int r;
+
+       if (ftrace_pid_trace >= 0)
+               r = sprintf(buf, "%u\n", ftrace_pid_trace);
+       else
+               r = sprintf(buf, "no pid\n");
+
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+ftrace_pid_write(struct file *filp, const char __user *ubuf,
+                  size_t cnt, loff_t *ppos)
+{
+       char buf[64];
+       long val;
+       int ret;
+
+       if (cnt >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
+
+       buf[cnt] = 0;
+
+       ret = strict_strtol(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&ftrace_start_lock);
+       if (ret < 0) {
+               /* disable pid tracing */
+               if (ftrace_pid_trace < 0)
+                       goto out;
+               ftrace_pid_trace = -1;
+
+       } else {
+
+               if (ftrace_pid_trace == val)
+                       goto out;
+
+               ftrace_pid_trace = val;
+       }
+
+       /* update the function call */
+       ftrace_update_pid_func();
+       ftrace_startup_enable(0);
+
+ out:
+       mutex_unlock(&ftrace_start_lock);
+
+       return cnt;
+}
+
+static struct file_operations ftrace_pid_fops = {
+       .read = ftrace_pid_read,
+       .write = ftrace_pid_write,
+};
+
+static __init int ftrace_init_debugfs(void)
+{
+       struct dentry *d_tracer;
+       struct dentry *entry;
+
+       d_tracer = tracing_init_dentry();
+       if (!d_tracer)
+               return 0;
+
+       ftrace_init_dyn_debugfs(d_tracer);
+
+       entry = debugfs_create_file("set_ftrace_pid", 0644, d_tracer,
+                                   NULL, &ftrace_pid_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs "
+                          "'set_ftrace_pid' entry\n");
+       return 0;
+}
+
+fs_initcall(ftrace_init_debugfs);
+
 /**
  * ftrace_kill - kill ftrace
  *
@@ -1428,7 +1582,7 @@ int register_ftrace_function(struct ftrace_ops *ops)
        }
 
        ret = __register_ftrace_function(ops);
-       ftrace_startup();
+       ftrace_startup(0);
 
 out:
        mutex_unlock(&ftrace_sysctl_lock);
@@ -1447,7 +1601,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
 
        mutex_lock(&ftrace_sysctl_lock);
        ret = __unregister_ftrace_function(ops);
-       ftrace_shutdown();
+       ftrace_shutdown(0);
        mutex_unlock(&ftrace_sysctl_lock);
 
        return ret;
@@ -1496,14 +1650,15 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
        return ret;
 }
 
-#ifdef CONFIG_FUNCTION_RET_TRACER
-
-static atomic_t ftrace_retfunc_active;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
 
-/* The callback that hooks the return of a function */
-trace_function_return_t ftrace_function_return =
-                       (trace_function_return_t)ftrace_stub;
+static atomic_t ftrace_graph_active;
 
+/* The callbacks that hook a function */
+trace_func_graph_ret_t ftrace_graph_return =
+                       (trace_func_graph_ret_t)ftrace_stub;
+trace_func_graph_ent_t ftrace_graph_entry =
+                       (trace_func_graph_ent_t)ftrace_stub;
 
 /* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */
 static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
@@ -1549,7 +1704,7 @@ free:
 }
 
 /* Allocate a return stack for each task */
-static int start_return_tracing(void)
+static int start_graph_tracing(void)
 {
        struct ftrace_ret_stack **ret_stack_list;
        int ret;
@@ -1569,7 +1724,8 @@ static int start_return_tracing(void)
        return ret;
 }
 
-int register_ftrace_return(trace_function_return_t func)
+int register_ftrace_graph(trace_func_graph_ret_t retfunc,
+                       trace_func_graph_ent_t entryfunc)
 {
        int ret = 0;
 
@@ -1583,28 +1739,30 @@ int register_ftrace_return(trace_function_return_t func)
                ret = -EBUSY;
                goto out;
        }
-       atomic_inc(&ftrace_retfunc_active);
-       ret = start_return_tracing();
+       atomic_inc(&ftrace_graph_active);
+       ret = start_graph_tracing();
        if (ret) {
-               atomic_dec(&ftrace_retfunc_active);
+               atomic_dec(&ftrace_graph_active);
                goto out;
        }
        ftrace_tracing_type = FTRACE_TYPE_RETURN;
-       ftrace_function_return = func;
-       ftrace_startup();
+       ftrace_graph_return = retfunc;
+       ftrace_graph_entry = entryfunc;
+       ftrace_startup(FTRACE_START_FUNC_RET);
 
 out:
        mutex_unlock(&ftrace_sysctl_lock);
        return ret;
 }
 
-void unregister_ftrace_return(void)
+void unregister_ftrace_graph(void)
 {
        mutex_lock(&ftrace_sysctl_lock);
 
-       atomic_dec(&ftrace_retfunc_active);
-       ftrace_function_return = (trace_function_return_t)ftrace_stub;
-       ftrace_shutdown();
+       atomic_dec(&ftrace_graph_active);
+       ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
+       ftrace_graph_entry = (trace_func_graph_ent_t)ftrace_stub;
+       ftrace_shutdown(FTRACE_STOP_FUNC_RET);
        /* Restore normal tracing type */
        ftrace_tracing_type = FTRACE_TYPE_ENTER;
 
@@ -1612,9 +1770,9 @@ void unregister_ftrace_return(void)
 }
 
 /* Allocate a return stack for newly created task */
-void ftrace_retfunc_init_task(struct task_struct *t)
+void ftrace_graph_init_task(struct task_struct *t)
 {
-       if (atomic_read(&ftrace_retfunc_active)) {
+       if (atomic_read(&ftrace_graph_active)) {
                t->ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH
                                * sizeof(struct ftrace_ret_stack),
                                GFP_KERNEL);
@@ -1626,7 +1784,7 @@ void ftrace_retfunc_init_task(struct task_struct *t)
                t->ret_stack = NULL;
 }
 
-void ftrace_retfunc_exit_task(struct task_struct *t)
+void ftrace_graph_exit_task(struct task_struct *t)
 {
        struct ftrace_ret_stack *ret_stack = t->ret_stack;
 
@@ -1638,5 +1796,3 @@ void ftrace_retfunc_exit_task(struct task_struct *t)
 }
 #endif
 
-
-