From: Jiri Olsa <jolsa@kernel.org>
To: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
Martin KaFai Lau <kafai@fb.com>,
Eduard Zingerman <eddyz87@gmail.com>,
Song Liu <songliubraving@fb.com>, Yonghong Song <yhs@fb.com>,
Menglong Dong <menglong8.dong@gmail.com>,
Steven Rostedt <rostedt@kernel.org>
Subject: [PATCHv3 bpf-next 16/24] libbpf: Add support to create tracing multi link
Date: Mon, 16 Mar 2026 08:51:30 +0100 [thread overview]
Message-ID: <20260316075138.465430-17-jolsa@kernel.org> (raw)
In-Reply-To: <20260316075138.465430-1-jolsa@kernel.org>
Adding bpf_program__attach_tracing_multi function for attaching
tracing program to multiple functions.
struct bpf_link *
bpf_program__attach_tracing_multi(const struct bpf_program *prog,
const char *pattern,
const struct bpf_tracing_multi_opts *opts);
User can specify functions to attach with 'pattern' argument that
allows wildcards (*?' supported) or provide BTF ids of functions
in array directly via opts argument. These options are mutually
exclusive.
When using BTF ids, user can also provide cookie value for each
provided id/function, that can be retrieved later in bpf program
with bpf_get_attach_cookie helper. Each cookie value is paired with
provided BTF id with the same array index.
Adding support to auto attach programs with following sections:
fsession.multi/<pattern>
fsession.multi.s/<pattern>
fentry.multi/<pattern>
fexit.multi/<pattern>
fentry.multi.s/<pattern>
fexit.multi.s/<pattern>
The provided <pattern> is used as 'pattern' argument in
bpf_program__attach_kprobe_multi_opts function.
The <pattern> allows to specify optional kernel module name with
following syntax:
<module>:<function_pattern>
In order to attach tracing_multi link to a module functions:
- program must be loaded with 'module' btf fd
(in attr::attach_btf_obj_fd)
- bpf_program__attach_tracing_multi must either have
pattern with module spec or BTF ids from the module
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/lib/bpf/libbpf.c | 263 +++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 15 +++
tools/lib/bpf/libbpf.map | 1 +
3 files changed, 279 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 084699448a94..c62a2ea84f9b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -7716,6 +7716,69 @@ static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program
static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attach_name,
int *btf_obj_fd, int *btf_type_id);
+static inline bool is_tracing_multi(enum bpf_attach_type type)
+{
+ return type == BPF_TRACE_FENTRY_MULTI || type == BPF_TRACE_FEXIT_MULTI ||
+ type == BPF_TRACE_FSESSION_MULTI;
+}
+
+static const struct module_btf *find_attach_module(struct bpf_object *obj, const char *attach)
+{
+ const char *sep, *mod_name = NULL;
+ int i, mod_len, err;
+
+ /*
+ * We expect attach string in the form of either
+ * - function_pattern or
+ * - <module>:function_pattern
+ */
+ sep = strchr(attach, ':');
+ if (sep) {
+ mod_name = attach;
+ mod_len = sep - mod_name;
+ }
+ if (!mod_name)
+ return NULL;
+
+ err = load_module_btfs(obj);
+ if (err)
+ return NULL;
+
+ for (i = 0; i < obj->btf_module_cnt; i++) {
+ const struct module_btf *mod = &obj->btf_modules[i];
+
+ if (strncmp(mod->name, mod_name, mod_len) == 0 && mod->name[mod_len] == '\0')
+ return mod;
+ }
+ return NULL;
+}
+
+static int tracing_multi_mod_fd(struct bpf_program *prog, int *btf_obj_fd)
+{
+ const char *attach_name, *sep;
+ const struct module_btf *mod;
+
+ *btf_obj_fd = 0;
+ attach_name = strchr(prog->sec_name, '/');
+
+ /* Program with no details in spec, using kernel btf. */
+ if (!attach_name)
+ return 0;
+
+ /* Program with no module section, using kernel btf. */
+ sep = strchr(++attach_name, ':');
+ if (!sep)
+ return 0;
+
+ /* Program with module specified, get its btf fd. */
+ mod = find_attach_module(prog->obj, attach_name);
+ if (!mod)
+ return -EINVAL;
+
+ *btf_obj_fd = mod->fd;
+ return 0;
+}
+
/* this is called as prog->sec_def->prog_prepare_load_fn for libbpf-supported sec_defs */
static int libbpf_prepare_prog_load(struct bpf_program *prog,
struct bpf_prog_load_opts *opts, long cookie)
@@ -7779,6 +7842,18 @@ static int libbpf_prepare_prog_load(struct bpf_program *prog,
opts->attach_btf_obj_fd = btf_obj_fd;
opts->attach_btf_id = btf_type_id;
}
+
+ if (is_tracing_multi(prog->expected_attach_type)) {
+ int err, btf_obj_fd = 0;
+
+ err = tracing_multi_mod_fd(prog, &btf_obj_fd);
+ if (err < 0)
+ return err;
+
+ prog->attach_btf_obj_fd = btf_obj_fd;
+ opts->attach_btf_obj_fd = btf_obj_fd;
+ }
+
return 0;
}
@@ -9835,6 +9910,7 @@ static int attach_kprobe_session(const struct bpf_program *prog, long cookie, st
static int attach_uprobe_multi(const struct bpf_program *prog, long cookie, struct bpf_link **link);
static int attach_lsm(const struct bpf_program *prog, long cookie, struct bpf_link **link);
static int attach_iter(const struct bpf_program *prog, long cookie, struct bpf_link **link);
+static int attach_tracing_multi(const struct bpf_program *prog, long cookie, struct bpf_link **link);
static const struct bpf_sec_def section_defs[] = {
SEC_DEF("socket", SOCKET_FILTER, 0, SEC_NONE),
@@ -9883,6 +9959,12 @@ static const struct bpf_sec_def section_defs[] = {
SEC_DEF("fexit.s+", TRACING, BPF_TRACE_FEXIT, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_trace),
SEC_DEF("fsession+", TRACING, BPF_TRACE_FSESSION, SEC_ATTACH_BTF, attach_trace),
SEC_DEF("fsession.s+", TRACING, BPF_TRACE_FSESSION, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_trace),
+ SEC_DEF("fsession.multi+", TRACING, BPF_TRACE_FSESSION_MULTI, 0, attach_tracing_multi),
+ SEC_DEF("fsession.multi.s+", TRACING, BPF_TRACE_FSESSION_MULTI, SEC_SLEEPABLE, attach_tracing_multi),
+ SEC_DEF("fentry.multi+", TRACING, BPF_TRACE_FENTRY_MULTI, 0, attach_tracing_multi),
+ SEC_DEF("fexit.multi+", TRACING, BPF_TRACE_FEXIT_MULTI, 0, attach_tracing_multi),
+ SEC_DEF("fentry.multi.s+", TRACING, BPF_TRACE_FENTRY_MULTI, SEC_SLEEPABLE, attach_tracing_multi),
+ SEC_DEF("fexit.multi.s+", TRACING, BPF_TRACE_FEXIT_MULTI, SEC_SLEEPABLE, attach_tracing_multi),
SEC_DEF("freplace+", EXT, 0, SEC_ATTACH_BTF, attach_trace),
SEC_DEF("lsm+", LSM, BPF_LSM_MAC, SEC_ATTACH_BTF, attach_lsm),
SEC_DEF("lsm.s+", LSM, BPF_LSM_MAC, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_lsm),
@@ -12329,6 +12411,187 @@ bool btf_type_is_traceable_func(const struct btf *btf, const struct btf_type *t)
return true;
}
+static int
+collect_btf_func_ids_by_glob(const struct btf *btf, const char *pattern, __u32 **ids)
+{
+ __u32 type_id, nr_types = btf__type_cnt(btf);
+ size_t cap = 0, cnt = 0;
+
+ if (!pattern)
+ return -EINVAL;
+
+ for (type_id = 1; type_id < nr_types; type_id++) {
+ const struct btf_type *t = btf__type_by_id(btf, type_id);
+ const char *name;
+ int err;
+
+ if (btf_kind(t) != BTF_KIND_FUNC)
+ continue;
+ name = btf__name_by_offset(btf, t->name_off);
+ if (!name)
+ continue;
+
+ if (!glob_match(name, pattern))
+ continue;
+ if (!btf_type_is_traceable_func(btf, t))
+ continue;
+
+ err = libbpf_ensure_mem((void **) ids, &cap, sizeof(**ids), cnt + 1);
+ if (err) {
+ free(*ids);
+ return -ENOMEM;
+ }
+ (*ids)[cnt++] = type_id;
+ }
+
+ return cnt;
+}
+
+static int collect_func_ids_by_glob(struct bpf_object *obj, const char *pattern, __u32 **ids)
+{
+ const struct module_btf *mod;
+ struct btf *btf = NULL;
+ const char *sep;
+ int err;
+
+ err = bpf_object__load_vmlinux_btf(obj, true);
+ if (err)
+ return err;
+
+ /* In case we have module specified, we will find its btf and use that. */
+ sep = strchr(pattern, ':');
+ if (sep) {
+ mod = find_attach_module(obj, pattern);
+ if (!mod) {
+ err = -EINVAL;
+ goto cleanup;
+ }
+ btf = mod->btf;
+ pattern = sep + 1;
+ } else {
+ btf = obj->btf_vmlinux;
+ }
+
+ err = collect_btf_func_ids_by_glob(btf, pattern, ids);
+
+cleanup:
+ bpf_object_cleanup_btf(obj);
+ return err;
+}
+
+struct bpf_link *
+bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pattern,
+ const struct bpf_tracing_multi_opts *opts)
+{
+ LIBBPF_OPTS(bpf_link_create_opts, lopts);
+ int prog_fd, link_fd, err, cnt;
+ __u32 *ids, *free_ids = NULL;
+ struct bpf_link *link;
+ __u64 *cookies;
+
+ if (!OPTS_VALID(opts, bpf_tracing_multi_opts))
+ return libbpf_err_ptr(-EINVAL);
+
+ cnt = OPTS_GET(opts, cnt, 0);
+ ids = OPTS_GET(opts, ids, NULL);
+ cookies = OPTS_GET(opts, cookies, NULL);
+
+ if (!!ids != !!cnt)
+ return libbpf_err_ptr(-EINVAL);
+ if (pattern && (ids || cookies))
+ return libbpf_err_ptr(-EINVAL);
+ if (!pattern && !ids)
+ return libbpf_err_ptr(-EINVAL);
+
+ if (pattern) {
+ cnt = collect_func_ids_by_glob(prog->obj, pattern, &ids);
+ if (cnt < 0)
+ return libbpf_err_ptr(cnt);
+ if (cnt == 0)
+ return libbpf_err_ptr(-EINVAL);
+ free_ids = ids;
+ }
+
+ lopts.tracing_multi.ids = ids;
+ lopts.tracing_multi.cookies = cookies;
+ lopts.tracing_multi.cnt = cnt;
+
+ link = calloc(1, sizeof(*link));
+ if (!link) {
+ err = -ENOMEM;
+ goto error;
+ }
+ link->detach = &bpf_link__detach_fd;
+
+ prog_fd = bpf_program__fd(prog);
+ link_fd = bpf_link_create(prog_fd, 0, prog->expected_attach_type, &lopts);
+ if (link_fd < 0) {
+ err = -errno;
+ pr_warn("prog '%s': failed to attach: %s\n", prog->name, errstr(err));
+ goto error;
+ }
+ link->fd = link_fd;
+ free(free_ids);
+ return link;
+
+error:
+ free(link);
+ free(free_ids);
+ return libbpf_err_ptr(err);
+}
+
+static int attach_tracing_multi(const struct bpf_program *prog, long cookie, struct bpf_link **link)
+{
+ static const char *const prefixes[] = {
+ "fentry.multi",
+ "fexit.multi",
+ "fsession.multi",
+ "fentry.multi.s",
+ "fexit.multi.s",
+ "fsession.multi.s",
+ };
+ const char *spec = NULL;
+ char *pattern;
+ size_t i;
+ int n;
+
+ *link = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
+ size_t pfx_len;
+
+ if (!str_has_pfx(prog->sec_name, prefixes[i]))
+ continue;
+
+ pfx_len = strlen(prefixes[i]);
+ /* no auto-attach case of, e.g., SEC("fentry.multi") */
+ if (prog->sec_name[pfx_len] == '\0')
+ return 0;
+
+ if (prog->sec_name[pfx_len] != '/')
+ continue;
+
+ spec = prog->sec_name + pfx_len + 1;
+ break;
+ }
+
+ if (!spec) {
+ pr_warn("prog '%s': invalid section name '%s'\n",
+ prog->name, prog->sec_name);
+ return -EINVAL;
+ }
+
+ n = sscanf(spec, "%m[a-zA-Z0-9_.*?:]", &pattern);
+ if (n < 1) {
+ pr_warn("tracing multi pattern is invalid: %s\n", spec);
+ return -EINVAL;
+ }
+
+ *link = bpf_program__attach_tracing_multi(prog, pattern, NULL);
+ free(pattern);
+ return libbpf_get_error(*link);
+}
+
static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe,
const char *binary_path, size_t offset)
{
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index dfc37a615578..b677aea7e592 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -701,6 +701,21 @@ bpf_program__attach_ksyscall(const struct bpf_program *prog,
const char *syscall_name,
const struct bpf_ksyscall_opts *opts);
+struct bpf_tracing_multi_opts {
+ /* size of this struct, for forward/backward compatibility */
+ size_t sz;
+ __u32 *ids;
+ __u64 *cookies;
+ size_t cnt;
+ size_t :0;
+};
+
+#define bpf_tracing_multi_opts__last_field cnt
+
+LIBBPF_API struct bpf_link *
+bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pattern,
+ const struct bpf_tracing_multi_opts *opts);
+
struct bpf_uprobe_opts {
/* size of this struct, for forward/backward compatibility */
size_t sz;
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index d18fbcea7578..ff4d7b2c8a14 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -452,6 +452,7 @@ LIBBPF_1.7.0 {
bpf_map__set_exclusive_program;
bpf_map__exclusive_program;
bpf_prog_assoc_struct_ops;
+ bpf_program__attach_tracing_multi;
bpf_program__assoc_struct_ops;
btf__permute;
} LIBBPF_1.6.0;
--
2.53.0
next prev parent reply other threads:[~2026-03-16 7:54 UTC|newest]
Thread overview: 48+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-16 7:51 [PATCHv3 bpf-next 00/24] bpf: tracing_multi link Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 01/24] ftrace: Add ftrace_hash_count function Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 02/24] bpf: Use mutex lock pool for bpf trampolines Jiri Olsa
2026-03-16 8:35 ` bot+bpf-ci
2026-03-16 21:16 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 03/24] bpf: Add struct bpf_trampoline_ops object Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 04/24] bpf: Add struct bpf_tramp_node object Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 05/24] bpf: Factor fsession link to use struct bpf_tramp_node Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 06/24] bpf: Add multi tracing attach types Jiri Olsa
2026-03-19 16:31 ` kernel test robot
2026-03-19 18:29 ` kernel test robot
2026-03-16 7:51 ` [PATCHv3 bpf-next 07/24] bpf: Move sleepable verification code to btf_id_allow_sleepable Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 08/24] bpf: Add bpf_trampoline_multi_attach/detach functions Jiri Olsa
2026-03-16 8:35 ` bot+bpf-ci
2026-03-16 21:16 ` Jiri Olsa
2026-03-20 10:18 ` kernel test robot
2026-03-16 7:51 ` [PATCHv3 bpf-next 09/24] bpf: Add support for tracing multi link Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 10/24] bpf: Add support for tracing_multi link cookies Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 11/24] bpf: Add support for tracing_multi link session Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 12/24] bpf: Add support for tracing_multi link fdinfo Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 13/24] libbpf: Add bpf_object_cleanup_btf function Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 14/24] libbpf: Add bpf_link_create support for tracing_multi link Jiri Olsa
2026-03-16 8:35 ` bot+bpf-ci
2026-03-16 21:16 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 15/24] libbpf: Add btf_type_is_traceable_func function Jiri Olsa
2026-03-16 7:51 ` Jiri Olsa [this message]
2026-03-16 8:35 ` [PATCHv3 bpf-next 16/24] libbpf: Add support to create tracing multi link bot+bpf-ci
2026-03-16 21:16 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 17/24] selftests/bpf: Add tracing multi skel/pattern/ids attach tests Jiri Olsa
2026-03-17 3:04 ` Leon Hwang
2026-03-17 17:18 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 18/24] selftests/bpf: Add tracing multi skel/pattern/ids module " Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 19/24] selftests/bpf: Add tracing multi intersect tests Jiri Olsa
2026-03-17 3:05 ` Leon Hwang
2026-03-17 17:18 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 20/24] selftests/bpf: Add tracing multi cookies test Jiri Olsa
2026-03-17 3:06 ` Leon Hwang
2026-03-17 17:18 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 21/24] selftests/bpf: Add tracing multi session test Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 22/24] selftests/bpf: Add tracing multi attach fails test Jiri Olsa
2026-03-17 3:06 ` Leon Hwang
2026-03-17 17:19 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 23/24] selftests/bpf: Add tracing multi attach benchmark test Jiri Olsa
2026-03-17 3:09 ` Leon Hwang
2026-03-17 17:19 ` Jiri Olsa
2026-03-16 7:51 ` [PATCHv3 bpf-next 24/24] selftests/bpf: Add tracing multi attach rollback tests Jiri Olsa
2026-03-17 3:20 ` Leon Hwang
2026-03-17 17:19 ` Jiri Olsa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260316075138.465430-17-jolsa@kernel.org \
--to=jolsa@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=kafai@fb.com \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=menglong8.dong@gmail.com \
--cc=rostedt@kernel.org \
--cc=songliubraving@fb.com \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.