From nobody Mon Feb 9 23:16:09 2026 Received: from mail-qk1-f176.google.com (mail-qk1-f176.google.com [209.85.222.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EAC2478F2F for ; Sun, 18 Jan 2026 01:18:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768699110; cv=none; b=kkQiGWuJ5+7tCm0Vv7OkK/6Rcjw86y7AgId1j15DDdR3BqKSumTN6FAc3S2nEJq17lGxPMX1VHFI5vasGbNW0dglKzGvmyJWnxQHQ7Dl+c/pDCOyxd0OPEw9Ln2puTABGj33ugTlZo0oTSx4zBqEFeLh4WH6gtjN2wjRu8UkzmA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768699110; c=relaxed/simple; bh=BCBYGa3TPryM/GRbyXK+YdnO4CpcUQ+fZVY68WD+2I0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ihw/jnyuqCPEhCdjOKgbUI4/uQCir0vMEsyJJ20OK54HDQdghF3bA5pO6WCX5VCuJw68kGCaJqfl85UNaiISYgwEAENfztjffJc7Q3g/Qiq3N+kuIA2m/kaL6ipqw30guCZVkKixs1K+Nvuan0C7aklFCeTF5RYYndc7jmlqYy8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ZpsbUpj3; arc=none smtp.client-ip=209.85.222.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZpsbUpj3" Received: by mail-qk1-f176.google.com with SMTP id af79cd13be357-8c6a234f094so322160385a.2 for ; Sat, 17 Jan 2026 17:18:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768699106; x=1769303906; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UeU9WtZvrzGKMp4I/N6B6Rs+f85g1m5C1V/fZOteYUE=; b=ZpsbUpj3OhY8pF7FzbOD/PjlE+GjEpgnXNW9jW856MD/lHuDJbIc9b6vVNgGgYfjt4 WVM2JwLPhyU5lhGSBogVY0Hw5Wq/uEmaFmDp1OMCv7hsQ87gme2txVXcesN4ZGPBbHrp hplN7qL4hp8bnDnAtt63NUxp8JdSTFaqP9eJsL8cCR7iMHj/4sKmdYBMPm+dezjsjRUZ SOYuu4o7BVaNH8MYI+KW9WsZDnSm/aPdT9wDgCqL/fDdEvXfVpBlodhQ7YOdjqSRGeai 5wF22NEr+bkkfBR0WbX65iJCAjkm+Q/1UN5w8oCKRIAJxRGK1RNDHIjvOuMFoLd5fhfW pGZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768699106; x=1769303906; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=UeU9WtZvrzGKMp4I/N6B6Rs+f85g1m5C1V/fZOteYUE=; b=c8WiPP1sQdeyW8QGaHAAZom9+Ytx6Qw8ierTnTPgsiJ3VMlbuGyGHnA8I1dpHgz5Fl nQ9/RY9Imw6xe6xIeTsS6g6y2QQ5QF5A/chCbOUZy4tuwV7/Y7EZTSytnqsc6DLOsbhl V2Sh1Vl8DfZVgUHTMEZY4jNtphYYv7/pehP3V4FmE2tARAm8xptizehB6ch+1n+MEupy deOVI+IYARwmEn5QNpQk/rO1Mt32NVKFkmfGzPa/X8RjtyZnBiRFk0vwNTf/gnwTIr/V n/c3tcMW8Bdyhg8T0cKQagSYIKEBmBHn7itAlZvCpwssVbYA9vWlqbuNPJjBzIrBpXNo HcUQ== X-Forwarded-Encrypted: i=1; AJvYcCWFk3GNzYmvZ9Lc4CRd4/5AYEio6FFzD0V79iB3hIB1Uy04yfNnLGJrIDdKz+OXe88en1RrAQQ46tPzniY=@vger.kernel.org X-Gm-Message-State: AOJu0YzUer909MHoqOLD9LK96Oy4d4AkQ7/5ARaJzlxJ7bVXisg9IGDz CBUcq+wtgkCnB6Af+cmV4rqeajN5D83dMlX5u2GhGCSr4EOfRI8aN3Gb X-Gm-Gg: AY/fxX6w2L4tZ1mQZf3OWVCnWXXDs2oR9+Pgknz7hcXM01apg9Y+LTlMfxWAle4cacY 2hJzfvHNwiCV4u/+CywIWaI7XPCuG+097WAw5/Ipv9S5Lt6DswVHIH7OEEFh7fRZDIDgsSVdd/G GQOW+Tv+6npRsIHyaBuH8u1gHR6uOdK7AfGVjEwjxBbw3/lUbhQOUinVIsEjzI3AJI3R3jbY5FA +QFc+P1ULs4mRFfCBzgd9JcZ+cH0h2/tTfAtWOTDRlAVyNL5fPqRFMwaEbU6XyUoRWa8y32BXnc cP+Qo7PfSS2nG/vlTe/TEfU8iJa0bgTAvBe04t81IJBb3At4n9OjLHLQL6g6IX5Q/kjWjoDf9eS yXa0otV94VxfqAjpT/1xVkzkejD9aQ/WpnAD/tjcsoI0xVxSYR73jlfkGE3wak61dJa2edgqqka nqGvMbHvZ7zON6QOOoNHARODLUaKWi1nkTrTXpFgI85/UoXX2h6C1C49GrVmzK X-Received: by 2002:a05:620a:46a5:b0:8b2:62f9:9fd8 with SMTP id af79cd13be357-8c6a678058emr1062620285a.61.1768699105669; Sat, 17 Jan 2026 17:18:25 -0800 (PST) Received: from pc.tail882856.ts.net ([2600:4041:4491:2000:8a64:ec6b:4:f1]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8c6a72642fdsm512290285a.46.2026.01.17.17.18.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Jan 2026 17:18:25 -0800 (PST) From: "Seokwoo Chung (Ryan)" To: rostedt@goodmis.org, mhiramat@kernel.org, corbet@lwn.net, shuah@kernel.org Cc: mathieu.desnoyers@efficios.com, linux-kernel@vger.kernel.org, linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, "Seokwoo Chung (Ryan)" Subject: [PATCH v5 v5 2/3] tracing/fprobe: Support comma-separated symbols and :entry/:exit Date: Sat, 17 Jan 2026 20:18:14 -0500 Message-ID: <20260118011815.56516-3-seokwoo.chung130@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260118011815.56516-1-seokwoo.chung130@gmail.com> References: <20260118011815.56516-1-seokwoo.chung130@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" - Update DEFINE_FREE to use standard __free() - Extend fprobe to support multiple symbols per event. Add parsing logic for lists, ! exclusions, and explicit suffixes. Update tracefs/README to refl= ect the new syntax Signed-off-by: Seokwoo Chung (Ryan) --- kernel/trace/trace.c | 3 +- kernel/trace/trace_fprobe.c | 209 +++++++++++++++++++++++++++--------- 2 files changed, 163 insertions(+), 49 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index baec63134ab6..10cdcc7b194e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5578,7 +5578,8 @@ static const char readme_msg[] =3D "\t r[maxactive][:[/][]] []\n" #endif #ifdef CONFIG_FPROBE_EVENTS - "\t f[:[/][]] [%return] []\n" + "\t f[:[/][]] [:entry|:exit] []\= n" + "\t (single symbols still accept %return)\n" "\t t[:[/][]] []\n" #endif #ifdef CONFIG_HIST_TRIGGERS diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index 262c0556e4af..5a2a41eea603 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -187,11 +187,14 @@ DEFINE_FREE(tuser_put, struct tracepoint_user *, */ struct trace_fprobe { struct dyn_event devent; + char *filter; struct fprobe fp; + bool list_mode; + char *nofilter; const char *symbol; + struct trace_probe tp; bool tprobe; struct tracepoint_user *tuser; - struct trace_probe tp; }; =20 static bool is_trace_fprobe(struct dyn_event *ev) @@ -559,6 +562,8 @@ 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); } @@ -838,7 +843,12 @@ static int __register_trace_fprobe(struct trace_fprobe= *tf) if (trace_fprobe_is_tracepoint(tf)) return __regsiter_tracepoint_fprobe(tf); =20 - /* 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); } =20 @@ -1154,60 +1164,119 @@ static struct notifier_block tprobe_event_module_n= b =3D { }; #endif /* CONFIG_MODULES */ =20 -static int parse_symbol_and_return(int argc, const char *argv[], - char **symbol, bool *is_return, - bool is_tracepoint) +static bool has_wildcard(const char *s) { - char *tmp =3D strchr(argv[1], '%'); - int i; + return s && (strchr(s, '*') || strchr(s, '?')); +} =20 - if (tmp) { - int len =3D tmp - argv[1]; +static int parse_fprobe_spec(const char *in, bool is_tracepoint, + char **base, bool *is_return, bool *list_mode, + char **filter, char **nofilter) +{ + char *work __free(kfree) =3D NULL; + char *b __free(kfree) =3D NULL; + char *f __free(kfree) =3D NULL; + char *nf __free(kfree) =3D NULL; + bool legacy_ret =3D false; + bool list =3D false; + const char *p; + int ret =3D 0; =20 - if (!is_tracepoint && !strcmp(tmp, "%return")) { - *is_return =3D true; - } else { - trace_probe_log_err(len, BAD_ADDR_SUFFIX); - return -EINVAL; - } - *symbol =3D kmemdup_nul(argv[1], len, GFP_KERNEL); - } else - *symbol =3D kstrdup(argv[1], GFP_KERNEL); - if (!*symbol) - return -ENOMEM; + if (!in || !base || !is_return || !list_mode || !filter || !nofilter) + return -EINVAL; =20 - if (*is_return) - return 0; + *base =3D NULL; *filter =3D NULL; *nofilter =3D NULL; + *is_return =3D false; *list_mode =3D false; =20 if (is_tracepoint) { - tmp =3D *symbol; - while (*tmp && (isalnum(*tmp) || *tmp =3D=3D '_')) - tmp++; - if (*tmp) { - /* find a wrong character. */ - trace_probe_log_err(tmp - *symbol, BAD_TP_NAME); - kfree(*symbol); - *symbol =3D NULL; + if (strchr(in, ',') || strchr(in, ':')) return -EINVAL; - } + if (strstr(in, "%return")) + return -EINVAL; + for (p =3D in; *p; p++) + if (!isalnum(*p) && *p !=3D '_') + return -EINVAL; + b =3D kstrdup(in, GFP_KERNEL); + if (!b) + return -ENOMEM; + *base =3D no_free_ptr(b); + return 0; } =20 - /* If there is $retval, this should be a return fprobe. */ - for (i =3D 2; i < argc; i++) { - tmp =3D strstr(argv[i], "$retval"); - if (tmp && !isalnum(tmp[7]) && tmp[7] !=3D '_') { - if (is_tracepoint) { - trace_probe_log_set_index(i); - trace_probe_log_err(tmp - argv[i], RETVAL_ON_PROBE); - kfree(*symbol); - *symbol =3D NULL; + work =3D kstrdup(in, GFP_KERNEL); + if (!work) + return -ENOMEM; + + p =3D strstr(work, "%return"); + if (p && p[7] =3D=3D '\0') { + *is_return =3D true; + legacy_ret =3D true; + *(char *)p =3D '\0'; + } else { + /* + * If "symbol:entry" or "symbol:exit" is given, it is new + * style probe. + */ + p =3D strrchr(work, ':'); + if (p) { + if (!strcmp(p, ":exit")) { + *is_return =3D true; + *(char *)p =3D '\0'; + } else if (!strcmp(p, ":entry")) { + *(char *)p =3D '\0'; + } else { return -EINVAL; } - *is_return =3D true; - break; } } - return 0; + + list =3D !!strchr(work, ','); +=09 + if (list && legacy_ret) { + return -EINVAL; + } + + if (legacy_ret) + *is_return =3D true; + + b =3D kstrdup(work, GFP_KERNEL); + if (!b) + return -ENOMEM; + + if (list) { + char *tmp =3D b, *tok; + size_t fsz, nfsz; + + fsz =3D nfsz =3D strlen(b) + 1; + + f =3D kzalloc(fsz, GFP_KERNEL); + nf =3D kzalloc(nfsz, GFP_KERNEL); + if (!f || !nf) + return -ENOMEM; + + while ((tok =3D strsep(&tmp, ",")) !=3D NULL) { + char *dst; + bool neg =3D (*tok =3D=3D '!'); + + if (*tok =3D=3D '\0') { + trace_probe_log_err(tmp - b - 1, BAD_TP_NAME); + return -EINVAL; + + if (neg) + tok++; + dst =3D neg ? nf : f; + if (dst[0] !=3D '\0') + strcat(dst, ","); + strcat(dst, tok); + } + *list_mode =3D true; + } + + *base =3D no_free_ptr(b); + *filter =3D no_free_ptr(f); + *nofilter =3D no_free_ptr(nf); + + return ret; } =20 static int trace_fprobe_create_internal(int argc, const char *argv[], @@ -1241,6 +1310,8 @@ static int trace_fprobe_create_internal(int argc, con= st char *argv[], const char *event =3D NULL, *group =3D FPROBE_EVENT_SYSTEM; struct module *mod __free(module_put) =3D NULL; const char **new_argv __free(kfree) =3D NULL; + char *parsed_nofilter __free(kfree) =3D NULL; + char *parsed_filter __free(kfree) =3D NULL; char *symbol __free(kfree) =3D NULL; char *ebuf __free(kfree) =3D NULL; char *gbuf __free(kfree) =3D NULL; @@ -1249,6 +1320,7 @@ static int trace_fprobe_create_internal(int argc, con= st char *argv[], char *dbuf __free(kfree) =3D NULL; int i, new_argc =3D 0, ret =3D 0; bool is_tracepoint =3D false; + bool list_mode =3D false; bool is_return =3D false; =20 if ((argv[0][0] !=3D 'f' && argv[0][0] !=3D 't') || argc < 2) @@ -1270,11 +1342,26 @@ static int trace_fprobe_create_internal(int argc, c= onst char *argv[], =20 trace_probe_log_set_index(1); =20 - /* a symbol(or tracepoint) must be specified */ - ret =3D parse_symbol_and_return(argc, argv, &symbol, &is_return, is_trace= point); + /* Parse spec early (single vs list, suffix, base symbol) */ + ret =3D parse_fprobe_spec(argv[1], is_tracepoint, &symbol, &is_return, + &list_mode, &parsed_filter, &parsed_nofilter); if (ret < 0) return -EINVAL; =20 + for (i =3D 2; i < argc; i++) { + char *tmp =3D strstr(argv[i], "$retval"); + + if (tmp && !isalnum(tmp[7]) && tmp[7] !=3D '_') { + if (is_tracepoint) { + trace_probe_log_set_index(i); + trace_probe_log_err(tmp - argv[i], RETVAL_ON_PROBE); + return -EINVAL; + } + is_return =3D true; + break; + } + } + trace_probe_log_set_index(0); if (event) { gbuf =3D kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL); @@ -1287,6 +1374,15 @@ static int trace_fprobe_create_internal(int argc, co= nst char *argv[], } =20 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; + } ebuf =3D kmalloc(MAX_EVENT_NAME_LEN, GFP_KERNEL); if (!ebuf) return -ENOMEM; @@ -1322,7 +1418,8 @@ static int trace_fprobe_create_internal(int argc, con= st char *argv[], NULL, NULL, NULL, sbuf); } } - if (!ctx->funcname) + + if (!list_mode && !has_wildcard(symbol) && !is_tracepoint) ctx->funcname =3D symbol; =20 abuf =3D kmalloc(MAX_BTF_ARGS_LEN, GFP_KERNEL); @@ -1356,6 +1453,21 @@ static int trace_fprobe_create_internal(int argc, co= nst char *argv[], return ret; } =20 + /* carry list parsing result into tf */ + if (!is_tracepoint) { + tf->list_mode =3D list_mode; + if (parsed_filter) { + tf->filter =3D kstrdup(parsed_filter, GFP_KERNEL); + if (!tf->filter) + return -ENOMEM; + } + if (parsed_nofilter) { + tf->nofilter =3D kstrdup(parsed_nofilter, GFP_KERNEL); + if (!tf->nofilter) + return -ENOMEM; + } + } + /* parse arguments */ for (i =3D 0; i < argc; i++) { trace_probe_log_set_index(i + 2); @@ -1442,8 +1554,9 @@ static int trace_fprobe_show(struct seq_file *m, stru= ct dyn_event *ev) seq_printf(m, ":%s/%s", trace_probe_group_name(&tf->tp), trace_probe_name(&tf->tp)); =20 - 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) && trace_fprobe_is_return(tf)) + seq_puts(m, ":exit"); =20 for (i =3D 0; i < tf->tp.nr_args; i++) seq_printf(m, " %s=3D%s", tf->tp.args[i].name, tf->tp.args[i].comm); --=20 2.43.0