ftrace: Create set_ftrace_notrace_pid to not trace tasks

There's currently a way to select a task that should only be traced by
functions, but there's no way to select a task not to be traced by the
function tracer. Add a set_ftrace_notrace_pid file that acts the same as
set_ftrace_pid (and is also affected by function-fork), but the task pids in
this file will not be traced even if they are listed in the set_ftrace_pid
file. This makes it easy for tools like trace-cmd to "hide" itself from the
function tracer when it is recording other tasks.
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 717e3f5e
...@@ -102,7 +102,7 @@ static bool ftrace_pids_enabled(struct ftrace_ops *ops) ...@@ -102,7 +102,7 @@ static bool ftrace_pids_enabled(struct ftrace_ops *ops)
tr = ops->private; tr = ops->private;
return tr->function_pids != NULL; return tr->function_pids != NULL || tr->function_no_pids != NULL;
} }
static void ftrace_update_trampoline(struct ftrace_ops *ops); static void ftrace_update_trampoline(struct ftrace_ops *ops);
...@@ -6931,10 +6931,12 @@ ftrace_filter_pid_sched_switch_probe(void *data, bool preempt, ...@@ -6931,10 +6931,12 @@ ftrace_filter_pid_sched_switch_probe(void *data, bool preempt,
{ {
struct trace_array *tr = data; struct trace_array *tr = data;
struct trace_pid_list *pid_list; struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
pid_list = rcu_dereference_sched(tr->function_pids); pid_list = rcu_dereference_sched(tr->function_pids);
no_pid_list = rcu_dereference_sched(tr->function_no_pids);
if (trace_ignore_this_task(pid_list, next)) if (trace_ignore_this_task(pid_list, no_pid_list, next))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid, this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE); FTRACE_PID_IGNORE);
else else
...@@ -6952,6 +6954,9 @@ ftrace_pid_follow_sched_process_fork(void *data, ...@@ -6952,6 +6954,9 @@ ftrace_pid_follow_sched_process_fork(void *data,
pid_list = rcu_dereference_sched(tr->function_pids); pid_list = rcu_dereference_sched(tr->function_pids);
trace_filter_add_remove_task(pid_list, self, task); trace_filter_add_remove_task(pid_list, self, task);
pid_list = rcu_dereference_sched(tr->function_no_pids);
trace_filter_add_remove_task(pid_list, self, task);
} }
static void static void
...@@ -6962,6 +6967,9 @@ ftrace_pid_follow_sched_process_exit(void *data, struct task_struct *task) ...@@ -6962,6 +6967,9 @@ ftrace_pid_follow_sched_process_exit(void *data, struct task_struct *task)
pid_list = rcu_dereference_sched(tr->function_pids); pid_list = rcu_dereference_sched(tr->function_pids);
trace_filter_add_remove_task(pid_list, NULL, task); trace_filter_add_remove_task(pid_list, NULL, task);
pid_list = rcu_dereference_sched(tr->function_no_pids);
trace_filter_add_remove_task(pid_list, NULL, task);
} }
void ftrace_pid_follow_fork(struct trace_array *tr, bool enable) void ftrace_pid_follow_fork(struct trace_array *tr, bool enable)
...@@ -6979,42 +6987,64 @@ void ftrace_pid_follow_fork(struct trace_array *tr, bool enable) ...@@ -6979,42 +6987,64 @@ void ftrace_pid_follow_fork(struct trace_array *tr, bool enable)
} }
} }
static void clear_ftrace_pids(struct trace_array *tr) enum {
TRACE_PIDS = BIT(0),
TRACE_NO_PIDS = BIT(1),
};
static void clear_ftrace_pids(struct trace_array *tr, int type)
{ {
struct trace_pid_list *pid_list; struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
int cpu; int cpu;
pid_list = rcu_dereference_protected(tr->function_pids, pid_list = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock)); lockdep_is_held(&ftrace_lock));
if (!pid_list) no_pid_list = rcu_dereference_protected(tr->function_no_pids,
lockdep_is_held(&ftrace_lock));
/* Make sure there's something to do */
if (!(((type & TRACE_PIDS) && pid_list) ||
((type & TRACE_NO_PIDS) && no_pid_list)))
return; return;
/* See if the pids still need to be checked after this */
if (!((!(type & TRACE_PIDS) && pid_list) ||
(!(type & TRACE_NO_PIDS) && no_pid_list))) {
unregister_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr); unregister_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
for_each_possible_cpu(cpu) for_each_possible_cpu(cpu)
per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = FTRACE_PID_TRACE; per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = FTRACE_PID_TRACE;
}
if (type & TRACE_PIDS)
rcu_assign_pointer(tr->function_pids, NULL); rcu_assign_pointer(tr->function_pids, NULL);
if (type & TRACE_NO_PIDS)
rcu_assign_pointer(tr->function_no_pids, NULL);
/* Wait till all users are no longer using pid filtering */ /* Wait till all users are no longer using pid filtering */
synchronize_rcu(); synchronize_rcu();
if ((type & TRACE_PIDS) && pid_list)
trace_free_pid_list(pid_list); trace_free_pid_list(pid_list);
if ((type & TRACE_NO_PIDS) && no_pid_list)
trace_free_pid_list(no_pid_list);
} }
void ftrace_clear_pids(struct trace_array *tr) void ftrace_clear_pids(struct trace_array *tr)
{ {
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
clear_ftrace_pids(tr); clear_ftrace_pids(tr, TRACE_PIDS | TRACE_NO_PIDS);
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
} }
static void ftrace_pid_reset(struct trace_array *tr) static void ftrace_pid_reset(struct trace_array *tr, int type)
{ {
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
clear_ftrace_pids(tr); clear_ftrace_pids(tr, type);
ftrace_update_pid_func(); ftrace_update_pid_func();
ftrace_startup_all(0); ftrace_startup_all(0);
...@@ -7078,9 +7108,45 @@ static const struct seq_operations ftrace_pid_sops = { ...@@ -7078,9 +7108,45 @@ static const struct seq_operations ftrace_pid_sops = {
.show = fpid_show, .show = fpid_show,
}; };
static int static void *fnpid_start(struct seq_file *m, loff_t *pos)
ftrace_pid_open(struct inode *inode, struct file *file) __acquires(RCU)
{
struct trace_pid_list *pid_list;
struct trace_array *tr = m->private;
mutex_lock(&ftrace_lock);
rcu_read_lock_sched();
pid_list = rcu_dereference_sched(tr->function_no_pids);
if (!pid_list)
return !(*pos) ? FTRACE_NO_PIDS : NULL;
return trace_pid_start(pid_list, pos);
}
static void *fnpid_next(struct seq_file *m, void *v, loff_t *pos)
{
struct trace_array *tr = m->private;
struct trace_pid_list *pid_list = rcu_dereference_sched(tr->function_no_pids);
if (v == FTRACE_NO_PIDS) {
(*pos)++;
return NULL;
}
return trace_pid_next(pid_list, v, pos);
}
static const struct seq_operations ftrace_no_pid_sops = {
.start = fnpid_start,
.next = fnpid_next,
.stop = fpid_stop,
.show = fpid_show,
};
static int pid_open(struct inode *inode, struct file *file, int type)
{ {
const struct seq_operations *seq_ops;
struct trace_array *tr = inode->i_private; struct trace_array *tr = inode->i_private;
struct seq_file *m; struct seq_file *m;
int ret = 0; int ret = 0;
...@@ -7091,9 +7157,18 @@ ftrace_pid_open(struct inode *inode, struct file *file) ...@@ -7091,9 +7157,18 @@ ftrace_pid_open(struct inode *inode, struct file *file)
if ((file->f_mode & FMODE_WRITE) && if ((file->f_mode & FMODE_WRITE) &&
(file->f_flags & O_TRUNC)) (file->f_flags & O_TRUNC))
ftrace_pid_reset(tr); ftrace_pid_reset(tr, type);
switch (type) {
case TRACE_PIDS:
seq_ops = &ftrace_pid_sops;
break;
case TRACE_NO_PIDS:
seq_ops = &ftrace_no_pid_sops;
break;
}
ret = seq_open(file, &ftrace_pid_sops); ret = seq_open(file, seq_ops);
if (ret < 0) { if (ret < 0) {
trace_array_put(tr); trace_array_put(tr);
} else { } else {
...@@ -7105,10 +7180,23 @@ ftrace_pid_open(struct inode *inode, struct file *file) ...@@ -7105,10 +7180,23 @@ ftrace_pid_open(struct inode *inode, struct file *file)
return ret; return ret;
} }
static int
ftrace_pid_open(struct inode *inode, struct file *file)
{
return pid_open(inode, file, TRACE_PIDS);
}
static int
ftrace_no_pid_open(struct inode *inode, struct file *file)
{
return pid_open(inode, file, TRACE_NO_PIDS);
}
static void ignore_task_cpu(void *data) static void ignore_task_cpu(void *data)
{ {
struct trace_array *tr = data; struct trace_array *tr = data;
struct trace_pid_list *pid_list; struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
/* /*
* This function is called by on_each_cpu() while the * This function is called by on_each_cpu() while the
...@@ -7116,8 +7204,10 @@ static void ignore_task_cpu(void *data) ...@@ -7116,8 +7204,10 @@ static void ignore_task_cpu(void *data)
*/ */
pid_list = rcu_dereference_protected(tr->function_pids, pid_list = rcu_dereference_protected(tr->function_pids,
mutex_is_locked(&ftrace_lock)); mutex_is_locked(&ftrace_lock));
no_pid_list = rcu_dereference_protected(tr->function_no_pids,
mutex_is_locked(&ftrace_lock));
if (trace_ignore_this_task(pid_list, current)) if (trace_ignore_this_task(pid_list, no_pid_list, current))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid, this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE); FTRACE_PID_IGNORE);
else else
...@@ -7126,12 +7216,13 @@ static void ignore_task_cpu(void *data) ...@@ -7126,12 +7216,13 @@ static void ignore_task_cpu(void *data)
} }
static ssize_t static ssize_t
ftrace_pid_write(struct file *filp, const char __user *ubuf, pid_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos, int type)
{ {
struct seq_file *m = filp->private_data; struct seq_file *m = filp->private_data;
struct trace_array *tr = m->private; struct trace_array *tr = m->private;
struct trace_pid_list *filtered_pids = NULL; struct trace_pid_list *filtered_pids;
struct trace_pid_list *other_pids;
struct trace_pid_list *pid_list; struct trace_pid_list *pid_list;
ssize_t ret; ssize_t ret;
...@@ -7140,19 +7231,39 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, ...@@ -7140,19 +7231,39 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
switch (type) {
case TRACE_PIDS:
filtered_pids = rcu_dereference_protected(tr->function_pids, filtered_pids = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock)); lockdep_is_held(&ftrace_lock));
other_pids = rcu_dereference_protected(tr->function_no_pids,
lockdep_is_held(&ftrace_lock));
break;
case TRACE_NO_PIDS:
filtered_pids = rcu_dereference_protected(tr->function_no_pids,
lockdep_is_held(&ftrace_lock));
other_pids = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock));
break;
}
ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt); ret = trace_pid_write(filtered_pids, &pid_list, ubuf, cnt);
if (ret < 0) if (ret < 0)
goto out; goto out;
switch (type) {
case TRACE_PIDS:
rcu_assign_pointer(tr->function_pids, pid_list); rcu_assign_pointer(tr->function_pids, pid_list);
break;
case TRACE_NO_PIDS:
rcu_assign_pointer(tr->function_no_pids, pid_list);
break;
}
if (filtered_pids) { if (filtered_pids) {
synchronize_rcu(); synchronize_rcu();
trace_free_pid_list(filtered_pids); trace_free_pid_list(filtered_pids);
} else if (pid_list) { } else if (pid_list && !other_pids) {
/* Register a probe to set whether to ignore the tracing of a task */ /* Register a probe to set whether to ignore the tracing of a task */
register_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr); register_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
} }
...@@ -7175,6 +7286,20 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, ...@@ -7175,6 +7286,20 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
return ret; return ret;
} }
static ssize_t
ftrace_pid_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
return pid_write(filp, ubuf, cnt, ppos, TRACE_PIDS);
}
static ssize_t
ftrace_no_pid_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
return pid_write(filp, ubuf, cnt, ppos, TRACE_NO_PIDS);
}
static int static int
ftrace_pid_release(struct inode *inode, struct file *file) ftrace_pid_release(struct inode *inode, struct file *file)
{ {
...@@ -7193,10 +7318,20 @@ static const struct file_operations ftrace_pid_fops = { ...@@ -7193,10 +7318,20 @@ static const struct file_operations ftrace_pid_fops = {
.release = ftrace_pid_release, .release = ftrace_pid_release,
}; };
static const struct file_operations ftrace_no_pid_fops = {
.open = ftrace_no_pid_open,
.write = ftrace_no_pid_write,
.read = seq_read,
.llseek = tracing_lseek,
.release = ftrace_pid_release,
};
void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer) void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer)
{ {
trace_create_file("set_ftrace_pid", 0644, d_tracer, trace_create_file("set_ftrace_pid", 0644, d_tracer,
tr, &ftrace_pid_fops); tr, &ftrace_pid_fops);
trace_create_file("set_ftrace_notrace_pid", 0644, d_tracer,
tr, &ftrace_no_pid_fops);
} }
void __init ftrace_init_tracefs_toplevel(struct trace_array *tr, void __init ftrace_init_tracefs_toplevel(struct trace_array *tr,
......
...@@ -386,16 +386,22 @@ trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid) ...@@ -386,16 +386,22 @@ trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid)
* Returns false if @task should be traced. * Returns false if @task should be traced.
*/ */
bool bool
trace_ignore_this_task(struct trace_pid_list *filtered_pids, struct task_struct *task) trace_ignore_this_task(struct trace_pid_list *filtered_pids,
struct trace_pid_list *filtered_no_pids,
struct task_struct *task)
{ {
/* /*
* Return false, because if filtered_pids does not exist, * If filterd_no_pids is not empty, and the task's pid is listed
* all pids are good to trace. * in filtered_no_pids, then return true.
* Otherwise, if filtered_pids is empty, that means we can
* trace all tasks. If it has content, then only trace pids
* within filtered_pids.
*/ */
if (!filtered_pids)
return false;
return !trace_find_filtered_pid(filtered_pids, task->pid); return (filtered_pids &&
!trace_find_filtered_pid(filtered_pids, task->pid)) ||
(filtered_no_pids &&
trace_find_filtered_pid(filtered_no_pids, task->pid));
} }
/** /**
...@@ -5013,6 +5019,8 @@ static const char readme_msg[] = ...@@ -5013,6 +5019,8 @@ static const char readme_msg[] =
#ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_FUNCTION_TRACER
" set_ftrace_pid\t- Write pid(s) to only function trace those pids\n" " set_ftrace_pid\t- Write pid(s) to only function trace those pids\n"
"\t\t (function)\n" "\t\t (function)\n"
" set_ftrace_notrace_pid\t- Write pid(s) to not function trace those pids\n"
"\t\t (function)\n"
#endif #endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
" set_graph_function\t- Trace the nested calls of a function (function_graph)\n" " set_graph_function\t- Trace the nested calls of a function (function_graph)\n"
......
...@@ -331,6 +331,7 @@ struct trace_array { ...@@ -331,6 +331,7 @@ struct trace_array {
#ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_FUNCTION_TRACER
struct ftrace_ops *ops; struct ftrace_ops *ops;
struct trace_pid_list __rcu *function_pids; struct trace_pid_list __rcu *function_pids;
struct trace_pid_list __rcu *function_no_pids;
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
/* All of these are protected by the ftrace_lock */ /* All of these are protected by the ftrace_lock */
struct list_head func_probes; struct list_head func_probes;
...@@ -782,6 +783,7 @@ extern int pid_max; ...@@ -782,6 +783,7 @@ extern int pid_max;
bool trace_find_filtered_pid(struct trace_pid_list *filtered_pids, bool trace_find_filtered_pid(struct trace_pid_list *filtered_pids,
pid_t search_pid); pid_t search_pid);
bool trace_ignore_this_task(struct trace_pid_list *filtered_pids, bool trace_ignore_this_task(struct trace_pid_list *filtered_pids,
struct trace_pid_list *filtered_no_pids,
struct task_struct *task); struct task_struct *task);
void trace_filter_add_remove_task(struct trace_pid_list *pid_list, void trace_filter_add_remove_task(struct trace_pid_list *pid_list,
struct task_struct *self, struct task_struct *self,
......
...@@ -549,8 +549,8 @@ event_filter_pid_sched_switch_probe_pre(void *data, bool preempt, ...@@ -549,8 +549,8 @@ event_filter_pid_sched_switch_probe_pre(void *data, bool preempt,
pid_list = rcu_dereference_sched(tr->filtered_pids); pid_list = rcu_dereference_sched(tr->filtered_pids);
this_cpu_write(tr->array_buffer.data->ignore_pid, this_cpu_write(tr->array_buffer.data->ignore_pid,
trace_ignore_this_task(pid_list, prev) && trace_ignore_this_task(pid_list, NULL, prev) &&
trace_ignore_this_task(pid_list, next)); trace_ignore_this_task(pid_list, NULL, next));
} }
static void static void
...@@ -563,7 +563,7 @@ event_filter_pid_sched_switch_probe_post(void *data, bool preempt, ...@@ -563,7 +563,7 @@ event_filter_pid_sched_switch_probe_post(void *data, bool preempt,
pid_list = rcu_dereference_sched(tr->filtered_pids); pid_list = rcu_dereference_sched(tr->filtered_pids);
this_cpu_write(tr->array_buffer.data->ignore_pid, this_cpu_write(tr->array_buffer.data->ignore_pid,
trace_ignore_this_task(pid_list, next)); trace_ignore_this_task(pid_list, NULL, next));
} }
static void static void
...@@ -579,7 +579,7 @@ event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task) ...@@ -579,7 +579,7 @@ event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task)
pid_list = rcu_dereference_sched(tr->filtered_pids); pid_list = rcu_dereference_sched(tr->filtered_pids);
this_cpu_write(tr->array_buffer.data->ignore_pid, this_cpu_write(tr->array_buffer.data->ignore_pid,
trace_ignore_this_task(pid_list, task)); trace_ignore_this_task(pid_list, NULL, task));
} }
static void static void
...@@ -596,7 +596,7 @@ event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task) ...@@ -596,7 +596,7 @@ event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task)
/* Set tracing if current is enabled */ /* Set tracing if current is enabled */
this_cpu_write(tr->array_buffer.data->ignore_pid, this_cpu_write(tr->array_buffer.data->ignore_pid,
trace_ignore_this_task(pid_list, current)); trace_ignore_this_task(pid_list, NULL, current));
} }
static void __ftrace_clear_event_pids(struct trace_array *tr) static void __ftrace_clear_event_pids(struct trace_array *tr)
...@@ -1597,7 +1597,7 @@ static void ignore_task_cpu(void *data) ...@@ -1597,7 +1597,7 @@ static void ignore_task_cpu(void *data)
mutex_is_locked(&event_mutex)); mutex_is_locked(&event_mutex));
this_cpu_write(tr->array_buffer.data->ignore_pid, this_cpu_write(tr->array_buffer.data->ignore_pid,
trace_ignore_this_task(pid_list, current)); trace_ignore_this_task(pid_list, NULL, current));
} }
static ssize_t static ssize_t
......
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