This series aims to extend fprobe with list-style filters and a clear entry/exist qualifier. Users can now specify a comma-separated symbol list with ! exclusions, and use a spec-level suffix to select probe type:
- funcA*, !funcAB, funcC -> entry probes - funcA*, !funcAB, funcC:entry -> explicit entry - funcA*, !funcAB, funcC:exit -> return/exit across the whole list
For compatibility, %return remains supported for single, literal symbols. When a list or wildcard is used, an explicit [GROUP/EVENT is required and autogeneration is disabled. Autogen names are kept for single-symbol specs, with wildcard sanitization. For list/wildcard forms we set ctx->funcname = NULL so BTF lookups are not attempted.
The series moves parsing to the parse path, documents the new syntax, and adds selftests that accept valid list cases and reject empty tokens, stray commas, and %return mixed with lists or wildcards. Selftests also verify enable/disable flow and that entry+exit on the same set do not double-count attached functions.
Help wanted: This is my first time contributing ftrace selftests. I would appreciate comments and recommendations on test structure and coverage.
Basic coverage is included, but this likely needs broader testing across architectures. Feedback and additional test ideas are welcome.
Changes since v2: - Introduce spec-level: :entry/:exit; reject %return with lists/wildcards - Require explict [GROUP/]EVENT for list/wildcard; keep autogen only for single literal. - Sanitize autogen names for single-symbol wildcards - Set ctx->funcname = NULL for list/wildcard to bypass BTF - Move list parsing out of __register_trace_fprobe() and into the parse path - Update docs and tracefs README and add dynevent selftests for accept/reject and enable/disable flow
Link: https://lore.kernel.org/lkml/20250904103219.f4937968362bfff1ecd3f004@kernel....
Ryan Chung (5): docs: tracing: fprobe: document list filters and :entry/:exit tracing: fprobe: require explicit [GROUP/]EVENT for list/wildcard tracing: fprobe: support comma-separated symbols and :entry/:exit selftests/ftrace: dynevent: add reject cases for list/:entry/:exit selftests/ftrace: dynevent: add reject cases
Documentation/trace/fprobetrace.rst | 27 +- kernel/trace/trace.c | 3 +- kernel/trace/trace_fprobe.c | 247 ++++++++++++++---- .../test.d/dynevent/add_remove_fprobe.tc | 121 +++++++++ .../test.d/dynevent/fprobe_syntax_errors.tc | 13 + 5 files changed, 349 insertions(+), 62 deletions(-)
Signed-off-by: Ryan Chung seokwoo.chung130@gmail.com --- Documentation/trace/fprobetrace.rst | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst index b4c2ca3d02c1..629e2d7402bd 100644 --- a/Documentation/trace/fprobetrace.rst +++ b/Documentation/trace/fprobetrace.rst @@ -25,21 +25,36 @@ Synopsis of fprobe-events ------------------------- ::
- f[:[GRP1/][EVENT1]] SYM [FETCHARGS] : Probe on function entry - f[MAXACTIVE][:[GRP1/][EVENT1]] SYM%return [FETCHARGS] : Probe on function exit - t[:[GRP2/][EVENT2]] TRACEPOINT [FETCHARGS] : Probe on tracepoint + # fprobe (function entry/exit) + f[:[GRP1/][EVENT1]] SYM_OR_LIST[:entry|:exit] [FETCHARGS] + + # legacy single-symbol exit + f[MAXACTIVE][:[GRP1/][EVENT1]] SYM%return [FETCHARGS] + + # Probe on tracepoint + t[:[GRP2/][EVENT2]] TRACEPOINT [FETCHARGS]
GRP1 : Group name for fprobe. If omitted, use "fprobes" for it. GRP2 : Group name for tprobe. If omitted, use "tracepoints" for it. - EVENT1 : Event name for fprobe. If omitted, the event name is - "SYM__entry" or "SYM__exit". + EVENT1 : Event name for fprobe. If omitted, + - For a single literal symbol, the event name is + "SYM__entry" or "SYM__exit". + - For a *list or any wildcard*, an explicit [GRP1/][EVENT1] + is required; otherwise the parser rejects it. EVENT2 : Event name for tprobe. If omitted, the event name is the same as "TRACEPOINT", but if the "TRACEPOINT" starts with a digit character, "_TRACEPOINT" is used. MAXACTIVE : Maximum number of instances of the specified function that can be probed simultaneously, or 0 for the default value as defined in Documentation/trace/fprobe.rst - + SYM_OR_LIST : Either a single symbol, or a comma-separated list of + include/exclude patterns: + - Tokens are matched as symbols; wildcards may be used. + - Tokens prefixed with '!' are exclusions. + - Examples: + foo # single literal (entry) + foo:exit # single literal exit + foo%return # legacy single-symbol exit FETCHARGS : Arguments. Each probe can have up to 128 args. ARG : Fetch "ARG" function argument using BTF (only for function entry or tracepoint.) (*1)
Signed-off-by: Ryan Chung seokwoo.chung130@gmail.com --- kernel/trace/trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index b3c94fbaf002..ac0d3acc337e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5524,7 +5524,8 @@ static const char readme_msg[] = "\t r[maxactive][:[<group>/][<event>]] <place> [<args>]\n" #endif #ifdef CONFIG_FPROBE_EVENTS - "\t f[:[<group>/][<event>]] <func-name>[%return] [<args>]\n" + "\t f[:[<group>/][<event>]] <func-name>[:entry|:exit] [<args>]\n" + "\t (single symbols still accept %return)\n" "\t t[:[<group>/][<event>]] <tracepoint> [<args>]\n" #endif #ifdef CONFIG_HIST_TRIGGERS
Signed-off-by: Ryan Chung seokwoo.chung130@gmail.com --- kernel/trace/trace_fprobe.c | 247 ++++++++++++++++++++++++++++-------- 1 file changed, 192 insertions(+), 55 deletions(-)
diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index b36ade43d4b3..ec5b6e1c1a1b 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -191,6 +191,9 @@ struct trace_fprobe { bool tprobe; struct tracepoint_user *tuser; struct trace_probe tp; + char *filter; + char *nofilter; + bool list_mode; };
static bool is_trace_fprobe(struct dyn_event *ev) @@ -203,14 +206,10 @@ static struct trace_fprobe *to_trace_fprobe(struct dyn_event *ev) return container_of(ev, struct trace_fprobe, devent); }
-/** - * for_each_trace_fprobe - iterate over the trace_fprobe list - * @pos: the struct trace_fprobe * for each entry - * @dpos: the struct dyn_event * to use as a loop cursor - */ -#define for_each_trace_fprobe(pos, dpos) \ - for_each_dyn_event(dpos) \ - if (is_trace_fprobe(dpos) && (pos = to_trace_fprobe(dpos))) +static struct trace_fprobe *trace_fprobe_from_dyn(struct dyn_event *ev) +{ + return is_trace_fprobe(ev) ? to_trace_fprobe(ev) : NULL; +}
static bool trace_fprobe_is_return(struct trace_fprobe *tf) { @@ -227,6 +226,109 @@ static const char *trace_fprobe_symbol(struct trace_fprobe *tf) return tf->symbol ? tf->symbol : "unknown"; }
+static bool has_wildcard(const char *s) +{ + return s && (strchr(s, '*') || strchr(s, '?')); +} + +static int parse_fprobe_spec(const char *in, bool is_tracepoint, + char **base, bool *is_return, bool *list_mode, + char **filter, char **nofilter) +{ + const char *p; + char *work = NULL; + char *b = NULL, *f = NULL, *nf = NULL; + bool legacy_ret = false; + bool list = false; + int ret = 0; + + if (!in || !base || !is_return || !list_mode || !filter || !nofilter) + return -EINVAL; + + *base = NULL; *filter = NULL; *nofilter = NULL; + *is_return = false; *list_mode = false; + + if (is_tracepoint) { + if (strchr(in, ',') || strchr(in, ':')) + return -EINVAL; + if (strstr(in, "%return")) + return -EINVAL; + for (p = in; *p; p++) + if (!isalnum(*p) && *p != '_') + return -EINVAL; + b = kstrdup(in, GFP_KERNEL); + if (!b) + return -ENOMEM; + *base = b; + return 0; + } + + work = kstrdup(in, GFP_KERNEL); + if (!work) + return -ENOMEM; + + p = strstr(work, "%return"); + if (p) { + if (!strcmp(p, ":exit")) { + *is_return = true; + *p = '\0'; + } else if (!strcmp(p, ":entry")) { + *p = '\0'; + } else { + ret = -EINVAL; + goto out; + } + } + + list = !!strchr(work, ',') || has_wildcard(work); + if (legacy_ret) + *is_return = true; + + b = kstrdup(work, GFP_KERNEL); + if (!b) { + ret = -ENOMEM; + goto out; + } + + if (list) { + char *tmp = b, *tok; + size_t fsz = strlen(b) + 1, nfsz = strlen(b) + 1; + + f = kzalloc(fsz, GFP_KERNEL); + nf = kzalloc(nfsz, GFP_KERNEL); + if (!f || !nf) { + ret = -ENOMEM; + goto out; + } + + while ((tok = strsep(&tmp, ",")) != NULL) { + char *dst; + bool neg = (*tok == '!'); + + if (*tok == '\0') + continue; + if (neg) + tok++; + dst = neg ? nf : f; + if (dst[0] != '\0') + strcat(dst, ","); + strcat(dst, tok); + } + *list_mode = true; + } + + *base = b; b = NULL; + *filter = f; f = NULL; + *nofilter = nf; nf = NULL; + +out: + kfree(work); + kfree(b); + kfree(f); + kfree(nf); + return ret; +} + static bool trace_fprobe_is_busy(struct dyn_event *ev) { struct trace_fprobe *tf = to_trace_fprobe(ev); @@ -556,13 +658,17 @@ static void free_trace_fprobe(struct trace_fprobe *tf) trace_probe_cleanup(&tf->tp); if (tf->tuser) tracepoint_user_put(tf->tuser); + kfree(tf->filter); + kfree(tf->nofilter); kfree(tf->symbol); kfree(tf); } }
/* Since alloc_trace_fprobe() can return error, check the pointer is ERR too. */ -DEFINE_FREE(free_trace_fprobe, struct trace_fprobe *, if (!IS_ERR_OR_NULL(_T)) free_trace_fprobe(_T)) +DEFINE_FREE(free_trace_fprobe, struct trace_fprobe *, + if (!IS_ERR_OR_NULL(_T)) + free_trace_fprobe(_T))
/* * Allocate new trace_probe and initialize it (including fprobe). @@ -605,10 +711,16 @@ static struct trace_fprobe *find_trace_fprobe(const char *event, struct dyn_event *pos; struct trace_fprobe *tf;
- for_each_trace_fprobe(tf, pos) + list_for_each_entry(pos, &dyn_event_list, list) { + tf = trace_fprobe_from_dyn(pos); + if (!tf) + continue; + if (strcmp(trace_probe_name(&tf->tp), event) == 0 && strcmp(trace_probe_group_name(&tf->tp), group) == 0) return tf; + } + return NULL; }
@@ -835,7 +947,12 @@ static int __register_trace_fprobe(struct trace_fprobe *tf) if (trace_fprobe_is_tracepoint(tf)) return __regsiter_tracepoint_fprobe(tf);
- /* TODO: handle filter, nofilter or symbol list */ + /* Registration path: + * - list_mode: pass filter/nofilter + * - single: pass symbol only (legacy) + */ + if (tf->list_mode) + return register_fprobe(&tf->fp, tf->filter, tf->nofilter); return register_fprobe(&tf->fp, tf->symbol, NULL); }
@@ -1114,7 +1231,11 @@ static int __tprobe_event_module_cb(struct notifier_block *self, return NOTIFY_DONE;
mutex_lock(&event_mutex); - for_each_trace_fprobe(tf, pos) { + list_for_each_entry(pos, &dyn_event_list, list) { + tf = trace_fprobe_from_dyn(pos); + if (!tf) + continue; + /* Skip fprobe and disabled tprobe events. */ if (!trace_fprobe_is_tracepoint(tf) || !tf->tuser) continue; @@ -1155,55 +1276,35 @@ static int parse_symbol_and_return(int argc, const char *argv[], char **symbol, bool *is_return, bool is_tracepoint) { - char *tmp = strchr(argv[1], '%'); - int i; - - if (tmp) { - int len = tmp - argv[1]; - - if (!is_tracepoint && !strcmp(tmp, "%return")) { - *is_return = true; - } else { - trace_probe_log_err(len, BAD_ADDR_SUFFIX); - return -EINVAL; - } - *symbol = kmemdup_nul(argv[1], len, GFP_KERNEL); - } else - *symbol = kstrdup(argv[1], GFP_KERNEL); - if (!*symbol) - return -ENOMEM; - - if (*is_return) - return 0; + int i, ret; + bool list_mode = false; + char *filter = NULL; *nofilter = NULL;
- if (is_tracepoint) { - tmp = *symbol; - while (*tmp && (isalnum(*tmp) || *tmp == '_')) - tmp++; - if (*tmp) { - /* find a wrong character. */ - trace_probe_log_err(tmp - *symbol, BAD_TP_NAME); - kfree(*symbol); - *symbol = NULL; - return -EINVAL; - } - } + ret = parse_fprobe_spec(argv[1], is_tracepoint, symbol, is_return, + &list_mode, &filter, &nofilter); + if (ret) + return ret;
- /* If there is $retval, this should be a return fprobe. */ for (i = 2; i < argc; i++) { - tmp = strstr(argv[i], "$retval"); + char *tmp = strstr(argv[i], "$retval"); + if (tmp && !isalnum(tmp[7]) && tmp[7] != '_') { if (is_tracepoint) { trace_probe_log_set_index(i); trace_probe_log_err(tmp - argv[i], RETVAL_ON_PROBE); kfree(*symbol); *symbol = NULL; + kfree(filter); + kfree(nofilter); return -EINVAL; } *is_return = true; break; } } + + kfree(filter); + kfree(nofilter); return 0; }
@@ -1247,6 +1348,11 @@ static int trace_fprobe_create_internal(int argc, const char *argv[], int i, new_argc = 0, ret = 0; bool is_tracepoint = false; bool is_return = false; + bool list_mode = false; + + char *parsed_filter __free(kfree) = NULL; + char *parsed_nofilter __free(kfree) = NULL; + bool has_wild = false;
if ((argv[0][0] != 'f' && argv[0][0] != 't') || argc < 2) return -ECANCELED; @@ -1267,8 +1373,9 @@ static int trace_fprobe_create_internal(int argc, const char *argv[],
trace_probe_log_set_index(1);
- /* a symbol(or tracepoint) must be specified */ - ret = parse_symbol_and_return(argc, argv, &symbol, &is_return, is_tracepoint); + /* Parse spec early (single vs list, suffix, base symbol) */ + ret = parse_fprobe_spec(argv[1], is_tracepoint, &symbol, &is_return, + &list_mode, &parsed_filter, &parsed_nofilter); if (ret < 0) return -EINVAL;
@@ -1283,10 +1390,16 @@ static int trace_fprobe_create_internal(int argc, const char *argv[], return -EINVAL; }
- if (!event) { - ebuf = kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL); - if (!ebuf) - return -ENOMEM; + if (!event) { + /* + * Event name rules: + * - For list/wildcard: require explicit [GROUP/]EVENT + * - For single literal: autogenerate symbol__entry/symbol__exit + */ + if (list_mode || has_wildcard(symbol)) { + trace_probe_log_err(0, NO_GROUP_NAME); + return -EINVAL; + } /* Make a new event name */ if (is_tracepoint) snprintf(ebuf, MAX_EVENT_NAME_LEN, "%s%s", @@ -1319,7 +1432,8 @@ static int trace_fprobe_create_internal(int argc, const char *argv[], NULL, NULL, NULL, sbuf); } } - if (!ctx->funcname) + + if (!list_mode && !has_wildcard(symbol) && !is_tracepoint) ctx->funcname = symbol;
abuf = kmalloc(MAX_BTF_ARGS_LEN, GFP_KERNEL); @@ -1353,6 +1467,21 @@ static int trace_fprobe_create_internal(int argc, const char *argv[], return ret; }
+ /* carry list parsing result into tf */ + if (!is_tracepoint) { + tf->list_mode = list_mode; + if (parsed_filter) { + tf->filter = kstrdup(parsed_filter, GFP_KERNEL); + if (!tf->filter) + return -ENOMEM; + } + if (parsed_nofilter) { + tf->nofilter = kstrdup(parsed_nofilter, GFP_KERNEL); + if (!tf->nofilter) + return -ENOMEM; + } + } + /* parse arguments */ for (i = 0; i < argc; i++) { trace_probe_log_set_index(i + 2); @@ -1439,8 +1568,16 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev) seq_printf(m, ":%s/%s", trace_probe_group_name(&tf->tp), trace_probe_name(&tf->tp));
- seq_printf(m, " %s%s", trace_fprobe_symbol(tf), - trace_fprobe_is_return(tf) ? "%return" : ""); + seq_printf(m, "%s", trace_fprobe_symbol(tf)); + if (!trace_fprobe_is_tracepoint(tf)) { + if (tf->list_mode) { + if (trace_fprobe_is_return(tf)) + seq_puts(m, ":exit"); + } else { + if (trace_fprobe_is_return(tf)) + seq_puts(m, "%return"); + } + }
for (i = 0; i < tf->tp.nr_args; i++) seq_printf(m, " %s=%s", tf->tp.args[i].name, tf->tp.args[i].comm);
Signed-off-by: Ryan Chung seokwoo.chung130@gmail.com --- .../test.d/dynevent/add_remove_fprobe.tc | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+)
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc index 2506f464811b..d5761d31217c 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc @@ -2,6 +2,8 @@ # SPDX-License-Identifier: GPL-2.0 # description: Generic dynamic event - add/remove fprobe events # requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README +# Note: list-style specs and :entry/:exit may be unavailable on older kernels. +# These tests auto-skip at runtime if the list form is rejected by tracefs.
echo 0 > events/enable echo > dynamic_events @@ -89,4 +91,123 @@ if [ $cnt -ne $ocnt ]; then exit_fail fi
+# ---- New accept cases for list syntax with :entry/:exit and !-exclusions ---- +if echo "f:test/__list_check $PLACE,$PLACE3" >> dynamic_events 2> /dev/null; then + # Clean the probe added by the guard + echo "-:test/__list_check" >> dynamic_events + + # List default (entry) with exclusion, explicit group/event + echo "f:test/list_entry $PLACE,!$PLACE2,$PLACE3" >> dynamic_events + grep -q "test/list_entry" dynamic_events + test -d events/test/list_entry + + echo 1 > events/test/list_entry/enable + # Should attach to PLACE and PLACE3, but not PLACE2 + grep -q "$PLACE" enabled_functions + grep -q "$PLACE3" enabled_functions + ! grep -q "$PLACE2" enabled_functions + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + # Disable and remove; count should be back to baseline + echo 0 > events/test/list_entry/enable + echo "-:test/list_entry" >> dynamic_events + ! grep -q "test/list_entry" dynamic_events + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $ocnt ]; then + exit_fail + fi + + # List with explicit :entry suffix (same behavior as default) + echo "f:test/list_entry_exp $PLACE,!$PLACE2,$PLACE3:entry" >> dynamic_events + grep -q "test/list_entry_exp" dynamic_events + test -d events/test/list_entry_exp + + echo 1 > events/test/list_entry_exp/enable + grep -q "$PLACE" enabled_functions + grep -q "$PLACE3" enabled_functions + ! grep -q "$PLACE2" enabled_functions + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + echo 0 > events/test/list_entry_exp/enable + echo "-:test/list_entry_exp" >> dynamic_events + ! grep -q "test/list_entry_exp" dynamic_events + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $ocnt ]; then + exit_fail + fi + + # List with :exit suffix across the same set + echo "f:test/list_exit $PLACE,!$PLACE2,$PLACE3:exit" >> dynamic_events + grep -q "test/list_exit" dynamic_events + test -d events/test/list_exit + + echo 1 > events/test/list_exit/enable + # On return probes, enabled_functions still reflects attached functions. + grep -q "$PLACE" enabled_functions + grep -q "$PLACE3" enabled_functions + ! grep -q "$PLACE2" enabled_functions + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + echo 0 > events/test/list_exit/enable + echo "-:test/list_exit" >> dynamic_events + ! grep -q "test/list_exit" dynamic_events + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $ocnt ]; then + exit_fail + fi + + # Enabling entry and exit together does not double-count functions + echo "f:test/list_both_e $PLACE,!$PLACE2,$PLACE3" >> dynamic_events + echo "f:test/list_both_x $PLACE,!$PLACE2,$PLACE3:exit" >> dynamic_events + grep -q "test/list_both_e" dynamic_events + grep -q "test/list_both_x" dynamic_events + test -d events/test/list_both_e + test -d events/test/list_both_x + + echo 1 > events/test/list_both_e/enable + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + # Enabling :exit for the same set should keep the count the same + echo 1 > events/test/list_both_x/enable + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + # Disable one; count should remain (the other still holds the attach) + echo 0 > events/test/list_both_e/enable + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $((ocnt + 2)) ]; then + exit_fail + fi + + # Disable the other; count returns to baseline + echo 0 > events/test/list_both_x/enable + cnt=`cat enabled_functions | wc -l` + if [ $cnt -ne $ocnt ]; then + exit_fail + fi + + # Remove both definitions + echo "-:test/list_both_e" >> dynamic_events + echo "-:test/list_both_x" >> dynamic_events + ! grep -q "test/list_both_e" dynamic_events + ! grep -q "test/list_both_x" dynamic_events +else + # List-form not supported; skip silently + : +fi + clear_trace
Signed-off-by: Ryan Chung seokwoo.chung130@gmail.com --- .../ftrace/test.d/dynevent/fprobe_syntax_errors.tc | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc index fee479295e2f..720c0047c0ff 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/fprobe_syntax_errors.tc @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 # description: Fprobe event parser error log check # requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[%return] [<args>]":README +# requires: dynamic_events "f[:[<group>/][<event>]] <func-name>[:entry|:exit] [<args>]":README
check_error() { # command-with-error-pos-by-^ ftrace_errlog_check 'trace_fprobe' "$1" 'dynamic_events' @@ -95,6 +96,18 @@ fi # %return suffix errors check_error 'f vfs_read^%hoge' # BAD_ADDR_SUFFIX
+# New list/wildcard syntax errors +if grep -q: ":exit" README; then +check_error 'f ^vfs_read, do_sys_open' # LIST_NEEDS_EVENT +check_error 'f ^vfs_read,do_sys_open' # LIST_NEEDS_EVENT +check_error 'f:dyn/ret_forbid vfs_*^%return' # WILDCARD_WITH_RETURN +check_error 'f:dyn/ret_forbid vfs_read,do_sys_open^%return' # LIST_WITH_RETURN +check_error 'f:dyn/list_bad ^,vfs_read' # LEADING_COMMA +check_error 'f:dyn/list_bad vfs_read,^' # TRAILING_COMMA +check_error 'f:dyn/list_bad vfs_read,^,do_sys_open' # EMPTY_TOKEN +check_error 'f:dyn/mixed vfs_read%return^:exit' # MIXED_SUFFIX + + # BTF arguments errors if grep -q "<argname>" README; then check_error 'f vfs_read args=^$arg*' # BAD_VAR_ARGS
linux-kselftest-mirror@lists.linaro.org