mirror of
https://github.com/torvalds/linux.git
synced 2024-11-29 15:41:36 +00:00
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: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
c2324b694f
commit
df4fc31558
@ -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
|
||||
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
|
||||
has processed and can trace. These are the function
|
||||
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,
|
||||
a search through /proc/mounts may be needed to find where the debugfs
|
||||
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
|
||||
--------------
|
||||
|
||||
|
@ -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
|
||||
@ -545,7 +615,19 @@ 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_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)
|
||||
{
|
||||
@ -558,16 +640,8 @@ 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;
|
||||
}
|
||||
ftrace_startup_enable(command);
|
||||
|
||||
if (!command || !ftrace_enabled)
|
||||
goto out;
|
||||
|
||||
ftrace_run_update_code(command);
|
||||
out:
|
||||
mutex_unlock(&ftrace_start_lock);
|
||||
}
|
||||
|
||||
@ -1262,13 +1336,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 +1366,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 +1451,100 @@ static int __init ftrace_nodyn_init(void)
|
||||
}
|
||||
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_shutdown() 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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user