* [PATCH v2 0/7] tracing: Parse filter from boot event string
@ 2026-09-08 6:22 Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 1/7] tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() Thomas Weißschuh
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh, stable
Make it possible to specify a trace event filter from the
kernel command line.
Example:
trace_event=syscalls:sys_enter_clock_nanosleep:filter:which_clock==0
As a preparation also add a KUnit test for the event string parsing.
Patch 1 is a bugfix which is already useful.
Patch 2 is a cleanup which is already useful.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
Changes in v2:
- Require an explicit :filter: prefix.
- Drop RFC prefix.
- Add MODULE_DESCRIPTION() to test module.
- Avoid invalid __free(kfree).
- Add documentation.
- Only allow filters from the boot command line.
- Move the filter to the very end of the event string and add a :filter: keyword.
- Add an example.
- Link to v1: https://patch.msgid.link/20260813-tracing-cli-event-filter-v1-0-57c4e8029c86@linutronix.de
---
Thomas Weißschuh (7):
tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event()
tracing: Remove duplicate declaration of ftrace_set_clr_event()
tracing: Stop modifying the input buffer in ftrace_set_clr_event()
tracing: Split the event string parsing logic into a dedicated function
tracing: Add a test for ftrace_parse_event_string()
tracing: Add a filter argument to __ftrace_set_clr_event()
tracing: Parse filter from boot event string
Documentation/admin-guide/kernel-parameters.txt | 9 +-
Documentation/trace/events.rst | 5 +
include/linux/trace_events.h | 2 +-
kernel/trace/Kconfig | 8 ++
kernel/trace/Makefile | 1 +
kernel/trace/trace.h | 6 +-
kernel/trace/trace_boot.c | 2 +-
kernel/trace/trace_events.c | 123 +++++++++++++++---------
kernel/trace/trace_events_test.c | 122 +++++++++++++++++++++++
9 files changed, 228 insertions(+), 50 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260812-tracing-cli-event-filter-7c91ee33a27e
Best regards,
--
Thomas Weißschuh <thomas.weissschuh@linutronix.de>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/7] tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event()
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 2/7] tracing: Remove duplicate declaration of ftrace_set_clr_event() Thomas Weißschuh
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh, stable
While ftrace_set_clr_event() modifies its input buffer during parsing,
before returning to the caller the buffer is supposed to be restored
to its original state.
This works correctly for the colon between the subsystem and event
but not the colon at the beginning of :mod:.
Restore the colon, so the :mod: trailer is not stripped after
ftrace_set_clr_event().
Fixes: 4c86bc531e60 ("tracing: Add :mod: command to enabled module events")
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
kernel/trace/trace_events.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 1d39eaf6a0f7..eab653cb3540 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1489,6 +1489,8 @@ int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
/* Put back the colon to allow this to be called again */
if (buf)
*(buf - 1) = ':';
+ if (mod)
+ *(mod - 5) = ':';
return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/7] tracing: Remove duplicate declaration of ftrace_set_clr_event()
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 1/7] tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 3/7] tracing: Stop modifying the input buffer in ftrace_set_clr_event() Thomas Weißschuh
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
The function is also declared in include/linux/trace_events.h which is
even included from kernel/trace/trace.h.
Having two declarations creates churn when changing the signature.
Remove the unnecessary, duplicate declaration.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
kernel/trace/trace.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 74a7a50d1e78..8fffd10fad53 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -2287,7 +2287,6 @@ static inline const char *get_syscall_name(int syscall)
void trace_event_init(void);
void trace_event_update_all(struct trace_eval_map **map, int len, struct module *mod);
/* Used from boot time tracer */
-extern int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
extern int trigger_process_regex(struct trace_event_file *file, char *buff);
#else
static inline void __init trace_event_init(void) { }
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 3/7] tracing: Stop modifying the input buffer in ftrace_set_clr_event()
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 1/7] tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 2/7] tracing: Remove duplicate declaration of ftrace_set_clr_event() Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 4/7] tracing: Split the event string parsing logic into a dedicated function Thomas Weißschuh
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
ftrace_set_clr_event() modifies its input buffer during parsing.
Before returning to the caller these modifications are undone.
This is restoration is error prone and was forgotten before.
Upcoming changes will also make it more complicated.
Copy the input argument and modify that copy and drop the
restoration logic.
Also mark the input argument as const.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
include/linux/trace_events.h | 2 +-
kernel/trace/trace_events.c | 19 ++++++++-----------
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 5cbd09c8be8d..af46614bc75d 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -882,7 +882,7 @@ extern int trace_add_event_call(struct trace_event_call *call);
extern int trace_remove_event_call(struct trace_event_call *call);
extern int trace_event_get_offsets(struct trace_event_call *call);
-int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
+int ftrace_set_clr_event(struct trace_array *tr, const char *buf, int set);
int trace_set_clr_event(const char *system, const char *event, int set);
int trace_array_set_clr_event(struct trace_array *tr, const char *system,
const char *event, bool enable);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index eab653cb3540..88d0212ff2ca 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1440,14 +1440,19 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
return ret;
}
-int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
+int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
{
char *event = NULL, *sub = NULL, *match, *mod;
- int ret;
+ char *buf;
if (!tr)
return -ENOENT;
+ char *dupped_buf __free(kfree) = kstrdup(arg_buf, GFP_KERNEL);
+ if (!dupped_buf)
+ return -ENOMEM;
+ buf = dupped_buf;
+
/* Modules events can be appended with :mod:<module> */
mod = strstr(buf, ":mod:");
if (mod) {
@@ -1484,15 +1489,7 @@ int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
match = NULL;
}
- ret = __ftrace_set_clr_event(tr, match, sub, event, set, mod);
-
- /* Put back the colon to allow this to be called again */
- if (buf)
- *(buf - 1) = ':';
- if (mod)
- *(mod - 5) = ':';
-
- return ret;
+ return __ftrace_set_clr_event(tr, match, sub, event, set, mod);
}
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 4/7] tracing: Split the event string parsing logic into a dedicated function
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
` (2 preceding siblings ...)
2026-09-08 6:22 ` [PATCH v2 3/7] tracing: Stop modifying the input buffer in ftrace_set_clr_event() Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 5/7] tracing: Add a test for ftrace_parse_event_string() Thomas Weißschuh
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
This new function can be tested with a KUnit test to prevent regressions
when adding new fields to the format.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
kernel/trace/trace_events.c | 61 +++++++++++++++++++++++++--------------------
1 file changed, 34 insertions(+), 27 deletions(-)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 88d0212ff2ca..4104ccbebf4a 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1440,25 +1440,18 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
return ret;
}
-int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
+static void
+ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod)
{
- char *event = NULL, *sub = NULL, *match, *mod;
- char *buf;
-
- if (!tr)
- return -ENOENT;
-
- char *dupped_buf __free(kfree) = kstrdup(arg_buf, GFP_KERNEL);
- if (!dupped_buf)
- return -ENOMEM;
- buf = dupped_buf;
+ *event = NULL;
+ *sub = NULL;
/* Modules events can be appended with :mod:<module> */
- mod = strstr(buf, ":mod:");
- if (mod) {
- *mod = '\0';
+ *mod = strstr(buf, ":mod:");
+ if (*mod) {
+ **mod = '\0';
/* move to the module name */
- mod += 5;
+ *mod += 5;
}
/*
@@ -1473,21 +1466,35 @@ int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
* the name <name> or any event that matches <name>
*/
- match = strsep(&buf, ":");
+ *match = strsep(&buf, ":");
if (buf) {
- sub = match;
- event = buf;
- match = NULL;
-
- if (!strlen(sub) || strcmp(sub, "*") == 0)
- sub = NULL;
- if (!strlen(event) || strcmp(event, "*") == 0)
- event = NULL;
- } else if (mod) {
+ *sub = *match;
+ *event = buf;
+ *match = NULL;
+
+ if (!strlen(*sub) || strcmp(*sub, "*") == 0)
+ *sub = NULL;
+ if (!strlen(*event) || strcmp(*event, "*") == 0)
+ *event = NULL;
+ } else if (*mod) {
/* Allow wildcard for no length or star */
- if (!strlen(match) || strcmp(match, "*") == 0)
- match = NULL;
+ if (!strlen(*match) || strcmp(*match, "*") == 0)
+ *match = NULL;
}
+}
+
+int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
+{
+ char *event, *sub, *match, *mod;
+
+ if (!tr)
+ return -ENOENT;
+
+ char *dupped_buf __free(kfree) = kstrdup(arg_buf, GFP_KERNEL);
+ if (!dupped_buf)
+ return -ENOMEM;
+
+ ftrace_parse_event_string(dupped_buf, &match, &sub, &event, &mod);
return __ftrace_set_clr_event(tr, match, sub, event, set, mod);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 5/7] tracing: Add a test for ftrace_parse_event_string()
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
` (3 preceding siblings ...)
2026-09-08 6:22 ` [PATCH v2 4/7] tracing: Split the event string parsing logic into a dedicated function Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 6/7] tracing: Add a filter argument to __ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 7/7] tracing: Parse filter from boot event string Thomas Weißschuh
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
The parsing logic is a bit complicated and about to become more so.
Add a unit test to avoid regressions.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
kernel/trace/Kconfig | 8 +++
kernel/trace/Makefile | 1 +
kernel/trace/trace.h | 4 ++
kernel/trace/trace_events.c | 5 +-
kernel/trace/trace_events_test.c | 102 +++++++++++++++++++++++++++++++++++++++
5 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 0ab5916575a9..430a4152095e 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -151,6 +151,14 @@ config EVENT_TRACING
select GLOB
bool
+config EVENT_TRACING_TEST
+ tristate "Test for event tracing" if !KUNIT_ALL_TESTS
+ depends on EVENT_TRACING
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ KUnit test for the event tracing implementation.
+
config CONTEXT_SWITCH_TRACER
bool
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index f934ff586bd4..7bef5a84ce47 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_EVENT_TRACING) += blktrace.o
endif
obj-$(CONFIG_EVENT_TRACING) += trace_events.o
obj-$(CONFIG_EVENT_TRACING) += trace_export.o
+obj-$(CONFIG_EVENT_TRACING_TEST) += trace_events_test.o
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
ifeq ($(CONFIG_PERF_EVENTS),y)
obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 8fffd10fad53..35f619a1655e 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -2506,3 +2506,7 @@ static inline int rv_init_interface(void)
})
#endif /* _LINUX_KERNEL_TRACE_H */
+
+#if IS_ENABLED(CONFIG_KUNIT)
+void ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod);
+#endif
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 4104ccbebf4a..7a491dbe6e81 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -25,6 +25,8 @@
#include <linux/delay.h>
#include <linux/btf.h>
+#include <kunit/visibility.h>
+
#include <trace/events/sched.h>
#include <trace/syscall.h>
@@ -1440,7 +1442,7 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
return ret;
}
-static void
+VISIBLE_IF_KUNIT void
ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod)
{
*event = NULL;
@@ -1482,6 +1484,7 @@ ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, cha
*match = NULL;
}
}
+EXPORT_SYMBOL_IF_KUNIT(ftrace_parse_event_string);
int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
{
diff --git a/kernel/trace/trace_events_test.c b/kernel/trace/trace_events_test.c
new file mode 100644
index 000000000000..515d2e668c31
--- /dev/null
+++ b/kernel/trace/trace_events_test.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+
+#include <kunit/test.h>
+
+#include "trace.h"
+
+struct parse_event_string_test_case {
+ const char *input;
+
+ const char *match, *sub, *event, *mod;
+};
+
+static const struct parse_event_string_test_case parse_event_string_test_cases[] = {
+ {
+ "",
+ .match = "",
+ },
+ {
+ "*:event",
+ .event = "event",
+ },
+ {
+ ":event",
+ .event = "event",
+ },
+ {
+ "sub:*",
+ .sub = "sub",
+ },
+ {
+ "sub:",
+ .sub = "sub",
+ },
+ {
+ "sub:event",
+ .sub = "sub",
+ .event = "event",
+ },
+ {
+ "match",
+ .match = "match",
+ },
+ {
+ ":mod:module",
+ .mod = "module",
+ },
+ {
+ "sub:event:mod:module",
+ .sub = "sub",
+ .event = "event",
+ .mod = "module",
+ },
+};
+
+static void
+parse_event_string_test_desc(const struct parse_event_string_test_case *params, char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "'%s'", params->input);
+}
+
+KUNIT_ARRAY_PARAM(parse_event_string, parse_event_string_test_cases, parse_event_string_test_desc);
+
+#define EXPECT_NULL_OR_STR_EQ(test, left, right) \
+({ \
+ if ((left) != NULL) \
+ KUNIT_EXPECT_STREQ(test, (left), (right)); \
+ else \
+ KUNIT_EXPECT_NULL(test, (right)); \
+})
+
+static void parse_event_string(struct kunit *test)
+{
+ const struct parse_event_string_test_case *params = test->param_value;
+ char *input, *match, *sub, *event, *mod;
+
+ input = kunit_kstrdup(test, params->input, GFP_KERNEL);
+ if (!input)
+ kunit_skip(test, "ENOMEM");
+
+ ftrace_parse_event_string(input, &match, &sub, &event, &mod);
+
+ EXPECT_NULL_OR_STR_EQ(test, match, params->match);
+ EXPECT_NULL_OR_STR_EQ(test, sub, params->sub);
+ EXPECT_NULL_OR_STR_EQ(test, event, params->event);
+ EXPECT_NULL_OR_STR_EQ(test, mod, params->mod);
+}
+
+static struct kunit_case trace_events_test_cases[] = {
+ KUNIT_CASE_PARAM(parse_event_string, parse_event_string_gen_params),
+ {}
+};
+
+static struct kunit_suite trace_events_test_suite = {
+ .name = "trace-events",
+ .test_cases = trace_events_test_cases,
+};
+kunit_test_suite(trace_events_test_suite);
+
+MODULE_DESCRIPTION("KUnit test for the event tracing implementation");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 6/7] tracing: Add a filter argument to __ftrace_set_clr_event()
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
` (4 preceding siblings ...)
2026-09-08 6:22 ` [PATCH v2 5/7] tracing: Add a test for ftrace_parse_event_string() Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 7/7] tracing: Parse filter from boot event string Thomas Weißschuh
6 siblings, 0 replies; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
Make it possible to directly enable a filter when enabling an event.
For now this is unused, an upcoming patch will wire it up.
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
kernel/trace/trace_events.c | 47 +++++++++++++++++++++++++++++----------------
1 file changed, 30 insertions(+), 17 deletions(-)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 7a491dbe6e81..f8881a9d3c7a 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -943,6 +943,7 @@ struct event_mod_load {
char *match;
char *system;
char *event;
+ char *filter;
};
static void free_event_mod(struct event_mod_load *event_mod)
@@ -952,6 +953,7 @@ static void free_event_mod(struct event_mod_load *event_mod)
kfree(event_mod->match);
kfree(event_mod->system);
kfree(event_mod->event);
+ kfree(event_mod->filter);
kfree(event_mod);
}
@@ -993,7 +995,7 @@ static int remove_cache_mod(struct trace_array *tr, const char *mod,
}
static int cache_mod(struct trace_array *tr, const char *mod, int set,
- const char *match, const char *system, const char *event)
+ const char *match, const char *system, const char *event, const char *filter)
{
struct event_mod_load *event_mod;
@@ -1032,6 +1034,12 @@ static int cache_mod(struct trace_array *tr, const char *mod, int set,
goto out_free;
}
+ if (filter) {
+ event_mod->filter = kstrdup(filter, GFP_KERNEL);
+ if (!event_mod->filter)
+ goto out_free;
+ }
+
list_add(&event_mod->list, &tr->mod_events);
return 0;
@@ -1044,7 +1052,7 @@ static int cache_mod(struct trace_array *tr, const char *mod, int set,
#else /* CONFIG_MODULES */
static inline void clear_mod_events(struct trace_array *tr) { }
static int cache_mod(struct trace_array *tr, const char *mod, int set,
- const char *match, const char *system, const char *event)
+ const char *match, const char *system, const char *event, const char *filter)
{
return -EINVAL;
}
@@ -1353,7 +1361,7 @@ static void remove_event_file_dir(struct trace_event_file *file)
static int
__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
const char *sub, const char *event, int set,
- const char *mod)
+ const char *mod, char *filter)
{
struct trace_event_file *file;
struct trace_event_call *call;
@@ -1403,7 +1411,11 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
if (event && strcmp(event, name) != 0)
continue;
- ret = ftrace_event_enable_disable(file, set);
+ if (filter)
+ ret = apply_event_filter(file, filter);
+
+ if (!filter || !ret)
+ ret = ftrace_event_enable_disable(file, set);
/*
* Save the first error and return that. Some events
@@ -1421,14 +1433,14 @@ __ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match,
* check if the module was loaded. If it wasn't cache it.
*/
if (module && ret == -EINVAL && !eret)
- ret = cache_mod(tr, module, set, match, sub, event);
+ ret = cache_mod(tr, module, set, match, sub, event, filter);
return ret;
}
static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
const char *sub, const char *event, int set,
- const char *mod)
+ const char *mod, char *filter)
{
int ret;
@@ -1436,7 +1448,7 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
return -EACCES;
mutex_lock(&event_mutex);
- ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
+ ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod, filter);
mutex_unlock(&event_mutex);
return ret;
@@ -1499,7 +1511,7 @@ int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
ftrace_parse_event_string(dupped_buf, &match, &sub, &event, &mod);
- return __ftrace_set_clr_event(tr, match, sub, event, set, mod);
+ return __ftrace_set_clr_event(tr, match, sub, event, set, mod, NULL);
}
/**
@@ -1521,7 +1533,7 @@ int trace_set_clr_event(const char *system, const char *event, int set)
if (!tr)
return -ENODEV;
- return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
+ return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL, NULL);
}
EXPORT_SYMBOL_GPL(trace_set_clr_event);
@@ -1547,7 +1559,7 @@ int trace_array_set_clr_event(struct trace_array *tr, const char *system,
return -ENOENT;
set = (enable == true) ? 1 : 0;
- return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL);
+ return __ftrace_set_clr_event(tr, NULL, system, event, set, NULL, NULL);
}
EXPORT_SYMBOL_GPL(trace_array_set_clr_event);
@@ -2067,7 +2079,7 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (system)
name = system->name;
- ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val, NULL);
+ ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val, NULL, NULL);
if (ret)
goto out;
@@ -4022,7 +4034,8 @@ static void update_mod_cache(struct trace_array *tr, struct module *mod)
__ftrace_set_clr_event_nolock(tr, event_mod->match,
event_mod->system,
- event_mod->event, 1, mod->name);
+ event_mod->event, 1, mod->name,
+ event_mod->filter);
free_event_mod(event_mod);
}
}
@@ -4796,7 +4809,7 @@ int event_trace_del_tracer(struct trace_array *tr)
__ftrace_clear_event_pids(tr, TRACE_PIDS | TRACE_NO_PIDS);
/* Disable any running events */
- __ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0, NULL);
+ __ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0, NULL, NULL);
/* Make sure no more events are being executed */
tracepoint_synchronize_unregister();
@@ -5106,7 +5119,7 @@ static __init void event_trace_self_tests(void)
pr_info("Testing event system %s: ", system->name);
- ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1, NULL);
+ ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1, NULL, NULL);
if (WARN_ON_ONCE(ret)) {
pr_warn("error enabling system %s\n",
system->name);
@@ -5115,7 +5128,7 @@ static __init void event_trace_self_tests(void)
event_test_stuff();
- ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0, NULL);
+ ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0, NULL, NULL);
if (WARN_ON_ONCE(ret)) {
pr_warn("error disabling system %s\n",
system->name);
@@ -5130,7 +5143,7 @@ static __init void event_trace_self_tests(void)
pr_info("Running tests on all trace events:\n");
pr_info("Testing all events: ");
- ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1, NULL);
+ ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1, NULL, NULL);
if (WARN_ON_ONCE(ret)) {
pr_warn("error enabling all events\n");
return;
@@ -5139,7 +5152,7 @@ static __init void event_trace_self_tests(void)
event_test_stuff();
/* reset sysname */
- ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0, NULL);
+ ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0, NULL, NULL);
if (WARN_ON_ONCE(ret)) {
pr_warn("error disabling all events\n");
return;
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 7/7] tracing: Parse filter from boot event string
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
` (5 preceding siblings ...)
2026-09-08 6:22 ` [PATCH v2 6/7] tracing: Add a filter argument to __ftrace_set_clr_event() Thomas Weißschuh
@ 2026-09-08 6:22 ` Thomas Weißschuh
2026-09-08 6:35 ` sashiko-bot
6 siblings, 1 reply; 9+ messages in thread
From: Thomas Weißschuh @ 2026-09-08 6:22 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu, Mathieu Desnoyers,
Jonathan Corbet
Cc: linux-doc, linux-kernel, linux-trace-kernel,
Thomas Weißschuh
Make it possible to specify a trace event filter from the
kernel command line.
Example:
trace_event=syscalls:sys_enter_clock_nanosleep:filter:which_clock==0
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
Documentation/admin-guide/kernel-parameters.txt | 9 +++++++--
Documentation/trace/events.rst | 5 +++++
include/linux/trace_events.h | 2 +-
kernel/trace/trace.h | 3 ++-
kernel/trace/trace_boot.c | 2 +-
kernel/trace/trace_events.c | 27 +++++++++++++++++--------
kernel/trace/trace_events_test.c | 26 +++++++++++++++++++++---
7 files changed, 58 insertions(+), 16 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd2..63347a986706 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -7769,8 +7769,13 @@ Kernel parameters
trace_event=:mod:<module>
The value before :mod: will only enable specific events
- that are part of the module. See the above mentioned
- document for more information.
+ that are part of the module.
+
+ To apply a filter, use the :filter: keyword:
+
+ trace_event=...:filter:<filter>
+
+ See the above mentioned document for more information.
trace_instance=[instance-info]
[FTRACE] Create a ring buffer instance early in boot up.
diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst
index 581f2260614b..6ce53372acbc 100644
--- a/Documentation/trace/events.rst
+++ b/Documentation/trace/events.rst
@@ -118,6 +118,11 @@ In order to facilitate early boot debugging, use boot option::
event-list is a comma separated list of events. See section 2.1 for event
format.
+The boot option also supports the ``:filter:`` keyword to apply a filter.
+The ``:filter:`` keyword needs to be at the end of the event specification::
+
+ <system>:<event>:mod:<module>:filter:<filter>
+
3. Defining an event-enabled tracepoint
=======================================
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index af46614bc75d..c1f6bb94f483 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -882,7 +882,7 @@ extern int trace_add_event_call(struct trace_event_call *call);
extern int trace_remove_event_call(struct trace_event_call *call);
extern int trace_event_get_offsets(struct trace_event_call *call);
-int ftrace_set_clr_event(struct trace_array *tr, const char *buf, int set);
+int ftrace_set_clr_event(struct trace_array *tr, const char *buf, int set, bool handle_filter);
int trace_set_clr_event(const char *system, const char *event, int set);
int trace_array_set_clr_event(struct trace_array *tr, const char *system,
const char *event, bool enable);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 35f619a1655e..2ec212d90192 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -2508,5 +2508,6 @@ static inline int rv_init_interface(void)
#endif /* _LINUX_KERNEL_TRACE_H */
#if IS_ENABLED(CONFIG_KUNIT)
-void ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod);
+void ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod,
+ bool handle_filter, char **filter);
#endif
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
index 5da0a8bbb110..6ba70dac1f06 100644
--- a/kernel/trace/trace_boot.c
+++ b/kernel/trace/trace_boot.c
@@ -94,7 +94,7 @@ trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node)
continue;
}
- if (ftrace_set_clr_event(tr, buf, 1) < 0)
+ if (ftrace_set_clr_event(tr, buf, 1, false) < 0)
pr_err("Failed to enable event: %s\n", p);
}
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index f8881a9d3c7a..2e2815d95c56 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1455,10 +1455,21 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
}
VISIBLE_IF_KUNIT void
-ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod)
+ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, char **mod,
+ bool handle_filter, char **filter)
{
*event = NULL;
*sub = NULL;
+ *filter = NULL;
+
+ /* event filters can be appended with :filter:<filter> */
+ if (handle_filter) {
+ *filter = strstr(buf, ":filter:");
+ if (*filter) {
+ **filter = '\0';
+ *filter += 8;
+ }
+ }
/* Modules events can be appended with :mod:<module> */
*mod = strstr(buf, ":mod:");
@@ -1498,9 +1509,9 @@ ftrace_parse_event_string(char *buf, char **match, char **sub, char **event, cha
}
EXPORT_SYMBOL_IF_KUNIT(ftrace_parse_event_string);
-int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
+int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set, bool handle_filter)
{
- char *event, *sub, *match, *mod;
+ char *event, *sub, *match, *mod, *filter;
if (!tr)
return -ENOENT;
@@ -1509,9 +1520,9 @@ int ftrace_set_clr_event(struct trace_array *tr, const char *arg_buf, int set)
if (!dupped_buf)
return -ENOMEM;
- ftrace_parse_event_string(dupped_buf, &match, &sub, &event, &mod);
+ ftrace_parse_event_string(dupped_buf, &match, &sub, &event, &mod, handle_filter, &filter);
- return __ftrace_set_clr_event(tr, match, sub, event, set, mod, NULL);
+ return __ftrace_set_clr_event(tr, match, sub, event, set, mod, filter);
}
/**
@@ -1593,7 +1604,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
if (*parser.buffer == '!')
set = 0;
- ret = ftrace_set_clr_event(tr, parser.buffer + !set, set);
+ ret = ftrace_set_clr_event(tr, parser.buffer + !set, set, false);
if (ret)
goto out_put;
}
@@ -4842,10 +4853,10 @@ static __init void __early_set_events(struct trace_array *tr, char *buf, bool en
while ((token = strsep(&buf, ","))) {
if (*token) {
if (enable) {
- if (ftrace_set_clr_event(tr, token, 1))
+ if (ftrace_set_clr_event(tr, token, 1, true))
pr_warn("Failed to enable trace event: %s\n", token);
} else {
- ftrace_set_clr_event(tr, token, 0);
+ ftrace_set_clr_event(tr, token, 0, true);
}
}
diff --git a/kernel/trace/trace_events_test.c b/kernel/trace/trace_events_test.c
index 515d2e668c31..4a2d77fa0f58 100644
--- a/kernel/trace/trace_events_test.c
+++ b/kernel/trace/trace_events_test.c
@@ -8,7 +8,7 @@
struct parse_event_string_test_case {
const char *input;
- const char *match, *sub, *event, *mod;
+ const char *match, *sub, *event, *mod, *filter;
};
static const struct parse_event_string_test_case parse_event_string_test_cases[] = {
@@ -37,6 +37,12 @@ static const struct parse_event_string_test_case parse_event_string_test_cases[]
.sub = "sub",
.event = "event",
},
+ {
+ "sub:event:filter:filter",
+ .sub = "sub",
+ .event = "event",
+ .filter = "filter",
+ },
{
"match",
.match = "match",
@@ -51,6 +57,19 @@ static const struct parse_event_string_test_case parse_event_string_test_cases[]
.event = "event",
.mod = "module",
},
+ {
+ "sub:event:mod:module:filter:filter",
+ .sub = "sub",
+ .event = "event",
+ .mod = "module",
+ .filter = "filter",
+ },
+ {
+ "sub:event:mod:module:filter:unexpected-filter",
+ .sub = "sub",
+ .event = "event",
+ .mod = "module:filter:unexpected-filter",
+ },
};
static void
@@ -72,18 +91,19 @@ KUNIT_ARRAY_PARAM(parse_event_string, parse_event_string_test_cases, parse_event
static void parse_event_string(struct kunit *test)
{
const struct parse_event_string_test_case *params = test->param_value;
- char *input, *match, *sub, *event, *mod;
+ char *input, *match, *sub, *event, *mod, *filter;
input = kunit_kstrdup(test, params->input, GFP_KERNEL);
if (!input)
kunit_skip(test, "ENOMEM");
- ftrace_parse_event_string(input, &match, &sub, &event, &mod);
+ ftrace_parse_event_string(input, &match, &sub, &event, &mod, params->filter, &filter);
EXPECT_NULL_OR_STR_EQ(test, match, params->match);
EXPECT_NULL_OR_STR_EQ(test, sub, params->sub);
EXPECT_NULL_OR_STR_EQ(test, event, params->event);
EXPECT_NULL_OR_STR_EQ(test, mod, params->mod);
+ EXPECT_NULL_OR_STR_EQ(test, filter, params->filter);
}
static struct kunit_case trace_events_test_cases[] = {
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2 7/7] tracing: Parse filter from boot event string
2026-09-08 6:22 ` [PATCH v2 7/7] tracing: Parse filter from boot event string Thomas Weißschuh
@ 2026-09-08 6:35 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-09-08 6:35 UTC (permalink / raw)
To: Thomas Weißschuh; +Cc: linux-trace-kernel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Implicit pointer-to-bool conversion in `trace_events_test.c` prevents testing `handle_filter=true` without a filter.
--
commit 125cd0f8d3ca4f597f1025a42dc567d0ce8c0b36
Author: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
tracing: Parse filter from boot event string
This patch enables specifying a trace event filter from the kernel command
line by parsing the :filter: keyword in the boot event string. It updates
the trace event parsing logic and the associated kunit tests to handle
the new syntax.
> diff --git a/kernel/trace/trace_events_test.c b/kernel/trace/trace_events_test.c
> --- a/kernel/trace/trace_events_test.c
> +++ b/kernel/trace/trace_events_test.c
[ ... ]
> @@ -72,18 +91,19 @@ KUNIT_ARRAY_PARAM(parse_event_string, parse_event_string_test_cases, parse_event
> static void parse_event_string(struct kunit *test)
> {
> const struct parse_event_string_test_case *params = test->param_value;
> - char *input, *match, *sub, *event, *mod;
> + char *input, *match, *sub, *event, *mod, *filter;
>
> input = kunit_kstrdup(test, params->input, GFP_KERNEL);
> if (!input)
> kunit_skip(test, "ENOMEM");
>
> - ftrace_parse_event_string(input, &match, &sub, &event, &mod);
> + ftrace_parse_event_string(input, &match, &sub, &event, &mod, params->filter, &filter);
[Severity: Low]
Does this pass params->filter (a const char pointer) to the bool handle_filter
argument of ftrace_parse_event_string()?
This implicitly casts the pointer to a boolean, meaning handle_filter is only
true when a filter string is expected in the test case output.
Consequently, could the test suite miss coverage for the common early boot
path where handle_filter is set to true, but no :filter: keyword is actually
present in the input string?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260908-tracing-cli-event-filter-v2-0-05396a3fb663@linutronix.de?part=7
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-09-08 6:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-08 6:22 [PATCH v2 0/7] tracing: Parse filter from boot event string Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 1/7] tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 2/7] tracing: Remove duplicate declaration of ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 3/7] tracing: Stop modifying the input buffer in ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 4/7] tracing: Split the event string parsing logic into a dedicated function Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 5/7] tracing: Add a test for ftrace_parse_event_string() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 6/7] tracing: Add a filter argument to __ftrace_set_clr_event() Thomas Weißschuh
2026-09-08 6:22 ` [PATCH v2 7/7] tracing: Parse filter from boot event string Thomas Weißschuh
2026-09-08 6:35 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox