On Thu, 1 Jan 2026 18:34:14 -0500 Aaron Tomlin <[email protected]> wrote:
> Currently, to audit active Ftrace event filters, userspace must > recursively traverse the events/ directory and read each individual > filter file. This is inefficient for monitoring tools and debugging. > > Introduce "show_event_filters" at the trace root directory. This file > displays all events that currently have a filter applied, alongside the > actual filter string, in a consolidated system:event [tab] filter > format. > > The implementation reuses the existing trace_event_file iterators to > ensure atomic traversal of the event list and utilises rcu_read_lock() > to safely access volatile filter strings. Nice. Perhaps we should do something similar for event triggers. > > Signed-off-by: Aaron Tomlin <[email protected]> > --- > Documentation/trace/ftrace.rst | 6 ++++ > kernel/trace/trace_events.c | 62 ++++++++++++++++++++++++++++++++++ > 2 files changed, 68 insertions(+) > > diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst > index 639f4d95732f..27ea54bfbc52 100644 > --- a/Documentation/trace/ftrace.rst > +++ b/Documentation/trace/ftrace.rst > @@ -684,6 +684,12 @@ of ftrace. Here is a list of some of the key files: > > See events.rst for more information. > > + show_event_filters: > + > + A list of events that are enabled and have a filter applied. Enabled? Or just events with filters. I think this should just say: A list of events that have filters. This shows the system/event pair along with the filter that is attached to the event. > + > + See events.rst for more information. > + > available_events: > > A list of events that can be enabled in tracing. > diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c > index b16a5a158040..f578ee2e5c12 100644 > --- a/kernel/trace/trace_events.c > +++ b/kernel/trace/trace_events.c > @@ -1661,6 +1661,34 @@ static void t_stop(struct seq_file *m, void *p) > mutex_unlock(&event_mutex); > } > > +/** > + * t_show_filters - seq_file callback to display active event filters > + * @m: The seq_file instance > + * @v: The current trace_event_file being iterated > + * > + * Traverses the trace_array event list and prints the system, name, > + * and filter string for any event with an active filter. This doesn't traverse the trace_array. The seq_file does the traversing. Just state that this is part of the seq_file output and shows events with filters. > + * Uses RCU to safely dereference the volatile filter pointer. This is internal to the function and should not be part of the kerneldoc. > + */ > +static int t_show_filters(struct seq_file *m, void *v) > +{ > + struct trace_event_file *file = v; > + struct trace_event_call *call = file->event_call; > + struct event_filter *filter; > + > + rcu_read_lock(); Use: guard(rcu)(); instead. > + filter = rcu_dereference(file->filter); > + if (filter && filter->filter_string) { > + seq_printf(m, "%s:%s\t%s\n", > + call->class->system, > + trace_event_name(call), > + filter->filter_string); > + } > + rcu_read_unlock(); And remove the rcu_read_unlock(). Actually, the function may be better by just doing: guard(rcu)(); filter = rcu_dereference(file->filter); if (!filter || !filter->filter_string) return 0; seq_printf(m, "%s:%s\t%s\n", call->class->system, trace_event_name(call), filter->filter_string); return 0; > + > + return 0; > +} > + > #ifdef CONFIG_MODULES > static int s_show(struct seq_file *m, void *v) > { > @@ -2488,6 +2516,7 @@ ftrace_event_npid_write(struct file *filp, const char > __user *ubuf, > > static int ftrace_event_avail_open(struct inode *inode, struct file *file); > static int ftrace_event_set_open(struct inode *inode, struct file *file); > +static int ftrace_event_show_filters_open(struct inode *inode, struct file > *file); > static int ftrace_event_set_pid_open(struct inode *inode, struct file *file); > static int ftrace_event_set_npid_open(struct inode *inode, struct file > *file); > static int ftrace_event_release(struct inode *inode, struct file *file); > @@ -2506,6 +2535,13 @@ static const struct seq_operations > show_set_event_seq_ops = { > .stop = s_stop, > }; > > +static const struct seq_operations show_show_event_filters_seq_ops = { > + .start = t_start, > + .next = t_next, > + .show = t_show_filters, > + .stop = t_stop, > +}; > + > static const struct seq_operations show_set_pid_seq_ops = { > .start = p_start, > .next = p_next, > @@ -2535,6 +2571,13 @@ static const struct file_operations > ftrace_set_event_fops = { > .release = ftrace_event_release, > }; > > +static const struct file_operations ftrace_show_event_filters_fops = { > + .open = ftrace_event_show_filters_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = seq_release, > +}; > + > static const struct file_operations ftrace_set_event_pid_fops = { > .open = ftrace_event_set_pid_open, > .read = seq_read, > @@ -2679,6 +2722,22 @@ ftrace_event_set_open(struct inode *inode, struct file > *file) > return ret; > } > > +/** > + * ftrace_event_show_filters_open - open interface for set_event_filters > + * @inode: the inode of the file > + * @file: the file being opened > + * > + * Connects the set_event_filters file to the sequence operations > + * required to iterate over and display active event filters. > + */ > +static int > +ftrace_event_show_filters_open(struct inode *inode, struct file *file) > +{ > + const struct seq_operations *seq_ops = &show_show_event_filters_seq_ops; > + > + return ftrace_event_open(inode, file, seq_ops); Why not just: return ftrace_event_open(inode, file, &show_show_event_filters_seq_ops); ? > +} > + > static int > ftrace_event_set_pid_open(struct inode *inode, struct file *file) > { > @@ -4399,6 +4458,9 @@ create_event_toplevel_files(struct dentry *parent, > struct trace_array *tr) > if (!entry) > return -ENOMEM; > > + trace_create_file("show_event_filters", TRACE_MODE_READ, parent, tr, > + &ftrace_show_event_filters_fops); > + > nr_entries = ARRAY_SIZE(events_entries); > > e_events = eventfs_create_events_dir("events", parent, events_entries, Thanks, -- Steve
