selftests/bpf: Add tracing_multi link info tests

Adding tracing_multi link info tests that follow the kprobe_multi
and uprobe_multi tests logic.

Assisted-by: Codex:GPT-5

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260629212208.895962-3-jolsa@kernel.org
This commit is contained in:
Jiri Olsa
2026-06-29 23:22:07 +02:00
committed by Andrii Nakryiko
parent 859055e076
commit d36e4dd547
2 changed files with 248 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
#include <string.h>
#include <linux/bpf.h>
#include <linux/limits.h>
#include <bpf/btf.h>
#include <test_progs.h>
#include "trace_helpers.h"
#include "test_fill_link_info.skel.h"
@@ -24,6 +25,22 @@ static __u64 kmulti_cookies[] = { 3, 1, 2 };
#define KPROBE_FUNC "bpf_fentry_test1"
static __u64 kprobe_addr;
static const char * const tmulti_syms[] = {
"bpf_fentry_test2",
"bpf_fentry_test1",
"bpf_fentry_test3",
};
static __u64 tmulti_cookies[] = { 30, 10, 20 };
#define TRACING_MULTI_CNT ARRAY_SIZE(tmulti_syms)
struct tmulti_target {
const char *name;
__u64 addr;
__u64 cookie;
__u32 id;
};
#define UPROBE_FILE "/proc/self/exe"
static ssize_t uprobe_offset;
/* uprobe attach point */
@@ -396,6 +413,224 @@ static void test_kprobe_multi_fill_link_info(struct test_fill_link_info *skel,
bpf_link__destroy(link);
}
static int tmulti_target_cmp(const void *a, const void *b)
{
const struct tmulti_target *ta = a;
const struct tmulti_target *tb = b;
return (ta->id > tb->id) - (ta->id < tb->id);
}
static int setup_tmulti_targets(const struct bpf_program *prog,
struct tmulti_target *targets,
__u32 *btf_obj_id)
{
struct bpf_prog_info prog_info;
__u32 len = sizeof(prog_info);
struct btf *btf;
int err, i;
__s32 id;
btf = btf__load_vmlinux_btf();
if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf"))
return -1;
for (i = 0; i < TRACING_MULTI_CNT; i++) {
id = btf__find_by_name_kind(btf, tmulti_syms[i], BTF_KIND_FUNC);
if (!ASSERT_GT(id, 0, "btf__find_by_name_kind"))
goto error;
targets[i].name = tmulti_syms[i];
targets[i].addr = ksym_get_addr(tmulti_syms[i]);
targets[i].cookie = tmulti_cookies[i];
targets[i].id = id;
}
memset(&prog_info, 0, len);
err = bpf_prog_get_info_by_fd(bpf_program__fd(prog), &prog_info, &len);
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
goto error;
if (!ASSERT_GT(prog_info.attach_btf_obj_id, 0, "attach_btf_obj_id"))
goto error;
*btf_obj_id = prog_info.attach_btf_obj_id;
/*
* The kernel tracing multi attach sorts ids. We sort as well,
* so we can easily compare ids and cookies later.
*/
qsort(targets, TRACING_MULTI_CNT, sizeof(targets[0]), tmulti_target_cmp);
btf__free(btf);
return 0;
error:
btf__free(btf);
return -1;
}
static int verify_tracing_multi_link_info(int fd, const struct bpf_program *prog,
const struct tmulti_target *targets,
__u32 btf_obj_id, bool has_cookies)
{
enum bpf_attach_type attach_type = bpf_program__expected_attach_type(prog);
__u64 addrs[TRACING_MULTI_CNT], cookies[TRACING_MULTI_CNT];
__u32 ids[TRACING_MULTI_CNT];
struct bpf_link_info info;
__u32 len = sizeof(info);
int err, i;
memset(&info, 0, sizeof(info));
err = bpf_link_get_info_by_fd(fd, &info, &len);
if (!ASSERT_OK(err, "bpf_link_get_info_by_fd"))
return -1;
if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_TRACING_MULTI, "info.type"))
return -1;
ASSERT_EQ(info.tracing_multi.attach_type, attach_type, "info.tracing_multi.attach_type");
ASSERT_EQ(info.tracing_multi.count, TRACING_MULTI_CNT, "info.tracing_multi.count");
memset(ids, 0, sizeof(ids));
memset(cookies, 0, sizeof(cookies));
memset(addrs, 0, sizeof(addrs));
info.tracing_multi.ids = ptr_to_u64(ids);
info.tracing_multi.addrs = ptr_to_u64(addrs);
info.tracing_multi.cookies = has_cookies ? ptr_to_u64(cookies) : 0;
info.tracing_multi.count = TRACING_MULTI_CNT;
err = bpf_link_get_info_by_fd(fd, &info, &len);
if (!ASSERT_OK(err, "bpf_link_get_info_by_fd"))
return -1;
if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_TRACING_MULTI, "info.type"))
return -1;
ASSERT_EQ(info.tracing_multi.attach_type, attach_type, "info.tracing_multi.attach_type");
ASSERT_EQ(info.tracing_multi.count, TRACING_MULTI_CNT, "info.tracing_multi.count");
ASSERT_EQ(info.tracing_multi.btf_obj_id, btf_obj_id, "tracing_multi.btf_obj_id");
for (i = 0; i < TRACING_MULTI_CNT; i++) {
ASSERT_EQ(ids[i], targets[i].id, "tracing_multi.ids");
ASSERT_EQ(cookies[i], has_cookies ? targets[i].cookie : 0, "tracing_multi.cookies");
if (targets[i].addr) {
struct ksym *ksym;
if (!ASSERT_NEQ(addrs[i], 0, "tracing_multi.addrs"))
return -1;
ksym = ksym_search(addrs[i]);
if (!ASSERT_OK_PTR(ksym, "ksym_search"))
return -1;
ASSERT_STREQ(ksym->name, targets[i].name, "tracing_multi.addr_name");
} else {
ASSERT_EQ(addrs[i], 0, "tracing_multi.addrs");
}
}
return 0;
}
static void verify_tracing_multi_invalid_user_buffer(int fd, const struct tmulti_target *targets)
{
__u32 ids[TRACING_MULTI_CNT] = {};
struct bpf_link_info info;
__u32 len = sizeof(info);
int err, i;
/* Wrong info setup (ids != NULL and cnt == 0) -> EINVAL */
memset(&info, 0, sizeof(info));
info.tracing_multi.ids = ptr_to_u64(ids);
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_EQ(err, -EINVAL, "tracing_multi.invalid_count");
/* Smaller than actual count provided -> ENOSPC */
memset(ids, 0, sizeof(ids));
memset(&info, 0, sizeof(info));
info.tracing_multi.ids = ptr_to_u64(ids);
info.tracing_multi.count = TRACING_MULTI_CNT - 1;
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_EQ(err, -ENOSPC, "tracing_multi.small_count");
for (i = 0; i < TRACING_MULTI_CNT - 1; i++)
ASSERT_EQ(ids[i], targets[i].id, "tracing_multi.partial_ids");
/* check that the last entry is not populated */
ASSERT_EQ(ids[i], 0, "tracing_multi.partial_ids");
/* Bigger than actual count provided -> OK */
memset(ids, 0, sizeof(ids));
memset(&info, 0, sizeof(info));
info.tracing_multi.ids = ptr_to_u64(ids);
info.tracing_multi.count = TRACING_MULTI_CNT + 1;
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_OK(err, "tracing_multi.big_count");
for (i = 0; i < TRACING_MULTI_CNT; i++)
ASSERT_EQ(ids[i], targets[i].id, "tracing_multi.ids");
/* Invalid ids pointer -> EFAULT */
memset(&info, 0, sizeof(info));
info.tracing_multi.ids = 0x1;
info.tracing_multi.count = TRACING_MULTI_CNT;
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_EQ(err, -EFAULT, "tracing_multi.bad_btf_ids");
/* Invalid cookies pointer -> EFAULT */
memset(&info, 0, sizeof(info));
info.tracing_multi.cookies = 0x1;
info.tracing_multi.count = TRACING_MULTI_CNT;
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_EQ(err, -EFAULT, "tracing_multi.bad_cookies");
/* Invalid addrs pointer -> EFAULT */
memset(&info, 0, sizeof(info));
info.tracing_multi.addrs = 0x1;
info.tracing_multi.count = TRACING_MULTI_CNT;
err = bpf_link_get_info_by_fd(fd, &info, &len);
ASSERT_EQ(err, -EFAULT, "tracing_multi.bad_addrs");
}
static void test_tracing_multi_fill_link_info(struct test_fill_link_info *skel,
bool has_cookies, bool invalid)
{
LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
struct tmulti_target targets[TRACING_MULTI_CNT];
__u32 ids[TRACING_MULTI_CNT], btf_obj_id;
__u64 cookies[TRACING_MULTI_CNT];
struct bpf_link *link;
int link_fd, err, i;
#ifndef __x86_64__
test__skip();
return;
#endif
if (setup_tmulti_targets(skel->progs.tmulti_run, targets, &btf_obj_id))
return;
for (i = 0; i < TRACING_MULTI_CNT; i++) {
ids[i] = targets[i].id;
cookies[i] = targets[i].cookie;
}
opts.ids = ids;
opts.cnt = TRACING_MULTI_CNT;
if (has_cookies)
opts.cookies = cookies;
link = bpf_program__attach_tracing_multi(skel->progs.tmulti_run, NULL, &opts);
if (!ASSERT_OK_PTR(link, "bpf_program__attach_tracing_multi"))
return;
link_fd = bpf_link__fd(link);
if (invalid) {
verify_tracing_multi_invalid_user_buffer(link_fd, targets);
} else {
err = verify_tracing_multi_link_info(link_fd, skel->progs.tmulti_run,
targets, btf_obj_id, has_cookies);
ASSERT_OK(err, "verify_tracing_multi_link_info");
}
bpf_link__destroy(link);
}
#define SEC(name) __attribute__((section(name), used))
static short uprobe_link_info_sema_1 SEC(".probes");
@@ -640,6 +875,13 @@ void test_fill_link_info(void)
if (test__start_subtest("kprobe_multi_invalid_ubuff"))
test_kprobe_multi_fill_link_info(skel, true, true, true);
if (test__start_subtest("tracing_multi_link_info")) {
test_tracing_multi_fill_link_info(skel, false, false);
test_tracing_multi_fill_link_info(skel, true, false);
}
if (test__start_subtest("tracing_multi_invalid_ubuff"))
test_tracing_multi_fill_link_info(skel, true, true);
if (test__start_subtest("uprobe_multi_link_info"))
test_uprobe_multi_fill_link_info(skel, false, false);
if (test__start_subtest("uretprobe_multi_link_info"))

View File

@@ -58,4 +58,10 @@ int BPF_PROG(umulti_run)
return 0;
}
SEC("fentry.multi")
int BPF_PROG(tmulti_run)
{
return 0;
}
char _license[] SEC("license") = "GPL";