Commit df4fc315 authored by Steven Rostedt's avatar Steven Rostedt Committed by Ingo Molnar

ftrace: add function tracing to single thread

Impact: feature to function trace a single thread

This patch adds the ability to function trace a single thread.
The file:

  /debugfs/tracing/set_ftrace_pid

contains the pid to trace. Valid pids are any positive integer.
Writing any negative number to this file will disable the pid
tracing and the function tracer will go back to tracing all of
threads.

This feature works with both static and dynamic function tracing.
Signed-off-by: default avatarSteven Rostedt <srostedt@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent c2324b69
...@@ -127,6 +127,8 @@ of ftrace. Here is a list of some of the key files: ...@@ -127,6 +127,8 @@ of ftrace. Here is a list of some of the key files:
be traced. If a function exists in both set_ftrace_filter be traced. If a function exists in both set_ftrace_filter
and set_ftrace_notrace, the function will _not_ be traced. and set_ftrace_notrace, the function will _not_ be traced.
set_ftrace_pid: Have the function tracer only trace a single thread.
available_filter_functions: This lists the functions that ftrace available_filter_functions: This lists the functions that ftrace
has processed and can trace. These are the function has processed and can trace. These are the function
names that you can pass to "set_ftrace_filter" or names that you can pass to "set_ftrace_filter" or
...@@ -1073,6 +1075,83 @@ For simple one time traces, the above is sufficent. For anything else, ...@@ -1073,6 +1075,83 @@ For simple one time traces, the above is sufficent. For anything else,
a search through /proc/mounts may be needed to find where the debugfs a search through /proc/mounts may be needed to find where the debugfs
file-system is mounted. file-system is mounted.
Single thread tracing
---------------------
By writing into /debug/tracing/set_ftrace_pid you can trace a
single thread. For example:
# cat /debug/tracing/set_ftrace_pid
no pid
# echo 3111 > /debug/tracing/set_ftrace_pid
# cat /debug/tracing/set_ftrace_pid
3111
# echo function > /debug/tracing/current_tracer
# cat /debug/tracing/trace | head
# tracer: function
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
yum-updatesd-3111 [003] 1637.254676: finish_task_switch <-thread_return
yum-updatesd-3111 [003] 1637.254681: hrtimer_cancel <-schedule_hrtimeout_range
yum-updatesd-3111 [003] 1637.254682: hrtimer_try_to_cancel <-hrtimer_cancel
yum-updatesd-3111 [003] 1637.254683: lock_hrtimer_base <-hrtimer_try_to_cancel
yum-updatesd-3111 [003] 1637.254685: fget_light <-do_sys_poll
yum-updatesd-3111 [003] 1637.254686: pipe_poll <-do_sys_poll
# echo -1 > /debug/tracing/set_ftrace_pid
# cat /debug/tracing/trace |head
# tracer: function
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
##### CPU 3 buffer started ####
yum-updatesd-3111 [003] 1701.957688: free_poll_entry <-poll_freewait
yum-updatesd-3111 [003] 1701.957689: remove_wait_queue <-free_poll_entry
yum-updatesd-3111 [003] 1701.957691: fput <-free_poll_entry
yum-updatesd-3111 [003] 1701.957692: audit_syscall_exit <-sysret_audit
yum-updatesd-3111 [003] 1701.957693: path_put <-audit_syscall_exit
If you want to trace a function when executing, you could use
something like this simple program:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main (int argc, char **argv)
{
if (argc < 1)
exit(-1);
if (fork() > 0) {
int fd, ffd;
char line[64];
int s;
ffd = open("/debug/tracing/current_tracer", O_WRONLY);
if (ffd < 0)
exit(-1);
write(ffd, "nop", 3);
fd = open("/debug/tracing/set_ftrace_pid", O_WRONLY);
s = sprintf(line, "%d\n", getpid());
write(fd, line, s);
write(ffd, "function", 8);
close(fd);
close(ffd);
execvp(argv[1], argv+1);
}
return 0;
}
dynamic ftrace dynamic ftrace
-------------- --------------
......
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
int ftrace_enabled __read_mostly; int ftrace_enabled __read_mostly;
static int last_ftrace_enabled; 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. */ /* Quick disabling of function tracer. */
int function_trace_stop; int function_trace_stop;
...@@ -61,6 +64,7 @@ static int ftrace_disabled __read_mostly; ...@@ -61,6 +64,7 @@ static int ftrace_disabled __read_mostly;
static DEFINE_SPINLOCK(ftrace_lock); static DEFINE_SPINLOCK(ftrace_lock);
static DEFINE_MUTEX(ftrace_sysctl_lock); static DEFINE_MUTEX(ftrace_sysctl_lock);
static DEFINE_MUTEX(ftrace_start_lock);
static struct ftrace_ops ftrace_list_end __read_mostly = static struct ftrace_ops ftrace_list_end __read_mostly =
{ {
...@@ -70,6 +74,7 @@ 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; 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_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) 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) ...@@ -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 * clear_ftrace_function - reset the ftrace function
* *
...@@ -96,6 +116,7 @@ void clear_ftrace_function(void) ...@@ -96,6 +116,7 @@ void clear_ftrace_function(void)
{ {
ftrace_trace_function = ftrace_stub; ftrace_trace_function = ftrace_stub;
__ftrace_trace_function = ftrace_stub; __ftrace_trace_function = ftrace_stub;
ftrace_pid_function = ftrace_stub;
} }
#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
...@@ -128,20 +149,26 @@ static int __register_ftrace_function(struct ftrace_ops *ops) ...@@ -128,20 +149,26 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
ftrace_list = ops; ftrace_list = ops;
if (ftrace_enabled) { 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 one func, simply call it directly.
* For more than one func, call the chain. * For more than one func, call the chain.
*/ */
#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
if (ops->next == &ftrace_list_end) ftrace_trace_function = func;
ftrace_trace_function = ops->func;
else
ftrace_trace_function = ftrace_list_func;
#else #else
if (ops->next == &ftrace_list_end) __ftrace_trace_function = func;
__ftrace_trace_function = ops->func;
else
__ftrace_trace_function = ftrace_list_func;
ftrace_trace_function = ftrace_test_stop_func; ftrace_trace_function = ftrace_test_stop_func;
#endif #endif
} }
...@@ -182,8 +209,19 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) ...@@ -182,8 +209,19 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
if (ftrace_enabled) { if (ftrace_enabled) {
/* If we only have one func left, then call that directly */ /* If we only have one func left, then call that directly */
if (ftrace_list->next == &ftrace_list_end) if (ftrace_list->next == &ftrace_list_end) {
ftrace_trace_function = ftrace_list->func; 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: out:
...@@ -192,6 +230,38 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) ...@@ -192,6 +230,38 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
return ret; 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 #ifdef CONFIG_DYNAMIC_FTRACE
#ifndef CONFIG_FTRACE_MCOUNT_RECORD #ifndef CONFIG_FTRACE_MCOUNT_RECORD
# error Dynamic ftrace depends on MCOUNT_RECORD # error Dynamic ftrace depends on MCOUNT_RECORD
...@@ -545,7 +615,19 @@ static void ftrace_run_update_code(int command) ...@@ -545,7 +615,19 @@ static void ftrace_run_update_code(int command)
static ftrace_func_t saved_ftrace_func; static ftrace_func_t saved_ftrace_func;
static int ftrace_start_up; static int ftrace_start_up;
static DEFINE_MUTEX(ftrace_start_lock);
static void ftrace_startup_enable(int command)
{
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(void) static void ftrace_startup(void)
{ {
...@@ -558,16 +640,8 @@ static void ftrace_startup(void) ...@@ -558,16 +640,8 @@ static void ftrace_startup(void)
ftrace_start_up++; ftrace_start_up++;
command |= FTRACE_ENABLE_CALLS; command |= FTRACE_ENABLE_CALLS;
if (saved_ftrace_func != ftrace_trace_function) { ftrace_startup_enable(command);
saved_ftrace_func = ftrace_trace_function;
command |= FTRACE_UPDATE_TRACE_FUNC;
}
if (!command || !ftrace_enabled)
goto out;
ftrace_run_update_code(command);
out:
mutex_unlock(&ftrace_start_lock); mutex_unlock(&ftrace_start_lock);
} }
...@@ -1262,13 +1336,10 @@ static struct file_operations ftrace_notrace_fops = { ...@@ -1262,13 +1336,10 @@ static struct file_operations ftrace_notrace_fops = {
.release = ftrace_notrace_release, .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; struct dentry *entry;
d_tracer = tracing_init_dentry();
entry = debugfs_create_file("available_filter_functions", 0444, entry = debugfs_create_file("available_filter_functions", 0444,
d_tracer, NULL, &ftrace_avail_fops); d_tracer, NULL, &ftrace_avail_fops);
if (!entry) if (!entry)
...@@ -1295,8 +1366,6 @@ static __init int ftrace_init_debugfs(void) ...@@ -1295,8 +1366,6 @@ static __init int ftrace_init_debugfs(void)
return 0; return 0;
} }
fs_initcall(ftrace_init_debugfs);
static int ftrace_convert_nops(struct module *mod, static int ftrace_convert_nops(struct module *mod,
unsigned long *start, unsigned long *start,
unsigned long *end) unsigned long *end)
...@@ -1382,12 +1451,100 @@ static int __init ftrace_nodyn_init(void) ...@@ -1382,12 +1451,100 @@ static int __init ftrace_nodyn_init(void)
} }
device_initcall(ftrace_nodyn_init); device_initcall(ftrace_nodyn_init);
static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }
static inline void ftrace_startup_enable(int command) { }
# define ftrace_startup() do { } while (0) # define ftrace_startup() do { } while (0)
# define ftrace_shutdown() do { } while (0) # define ftrace_shutdown() do { } while (0)
# define ftrace_startup_sysctl() do { } while (0) # define ftrace_startup_sysctl() do { } while (0)
# define ftrace_shutdown_sysctl() do { } while (0) # define ftrace_shutdown_sysctl() do { } while (0)
#endif /* CONFIG_DYNAMIC_FTRACE */ #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 * ftrace_kill - kill ftrace
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment