Commit c5f48c92 authored by Daniel Borkmann's avatar Daniel Borkmann
Browse files

Merge branch 'bpf-libbpf-link-trace'

Andrii Nakryiko says:

====================
This patchset adds the following APIs to allow attaching BPF programs to
tracing entities:
- bpf_program__attach_perf_event for attaching to any opened perf event FD,
  allowing users full control;
- bpf_program__attach_kprobe for attaching to kernel probes (both entry and
  return probes);
- bpf_program__attach_uprobe for attaching to user probes (both entry/return);
- bpf_program__attach_tracepoint for attaching to kernel tracepoints;
- bpf_program__attach_raw_tracepoint for attaching to raw kernel tracepoint
  (wrapper around bpf_raw_tracepoint_open);

This set of APIs makes libbpf more useful for tracing applications.

All attach APIs return abstract struct bpf_link that encapsulates logic of
detaching BPF program. See patch #2 for details. bpf_assoc was considered as
an alternative name for this opaque "handle", but bpf_link seems to be
appropriate semantically and is nice and short.

Pre-patch #1 makes internal libbpf_strerror_r helper function work w/ negative
error codes, lifting the burder off callers to keep track of error sign.
Patch #2 adds bpf_link abstraction.
Patch #3 adds attach_perf_event, which is the base for all other APIs.
Patch #4 adds kprobe/uprobe APIs.
Patch #5 adds tracepoint API.
Patch #6 adds raw_tracepoint API.
Patch #7 converts one existing test to use attach_perf_event.
Patch #8 adds new kprobe/uprobe tests.
Patch #9 converts some selftests currently using tracepoint to new APIs.

v4->v5:
- typo and small nits (Yonghong);
- validate pfd in attach_perf_event (Yonghong);
- parse_uint_from_file fixes (Yonghong);
- check for malloc failure in attach_raw_tracepoint (Yonghong);
- attach_probes selftests clean up fixes (Yonghong);
v3->v4:
- proper errno handling (Stanislav);
- bpf_fd -> prog_fd (Stanislav);
- switch to fprintf (Song);
v2->v3:
- added bpf_link concept (Daniel);
- didn't add generic bpf_link__attach_program for reasons described in [0];
- dropped Stanislav's Reviewed-by from patches #2-#6, in case he doesn't like
  the change;
v1->v2:
- preserve errno before close() call (Stanislav);
- use libbpf_perf_event_disable_and_close in selftest (Stanislav);
- remove unnecessary memset (Stanislav);

[0] https://lore.kernel.org/bpf/CAEf4BzZ7EM5eP2eaZn7T2Yb5QgVRiwAs+epeLR1g01TTx-6m6Q@mail.gmail.com/


====================

Acked-by: default avatarYonghong Song <yhs@fb.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents c4cde580 1bdb3427
Loading
Loading
Loading
Loading
+367 −0
Original line number Original line Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/limits.h>
#include <linux/limits.h>
#include <linux/perf_event.h>
#include <linux/perf_event.h>
#include <linux/ring_buffer.h>
#include <linux/ring_buffer.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <sys/vfs.h>
@@ -3941,6 +3942,372 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
	return 0;
	return 0;
}
}


struct bpf_link {
	int (*destroy)(struct bpf_link *link);
};

int bpf_link__destroy(struct bpf_link *link)
{
	int err;

	if (!link)
		return 0;

	err = link->destroy(link);
	free(link);

	return err;
}

struct bpf_link_fd {
	struct bpf_link link; /* has to be at the top of struct */
	int fd; /* hook FD */
};

static int bpf_link__destroy_perf_event(struct bpf_link *link)
{
	struct bpf_link_fd *l = (void *)link;
	int err;

	err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0);
	if (err)
		err = -errno;

	close(l->fd);
	return err;
}

struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
						int pfd)
{
	char errmsg[STRERR_BUFSIZE];
	struct bpf_link_fd *link;
	int prog_fd, err;

	if (pfd < 0) {
		pr_warning("program '%s': invalid perf event FD %d\n",
			   bpf_program__title(prog, false), pfd);
		return ERR_PTR(-EINVAL);
	}
	prog_fd = bpf_program__fd(prog);
	if (prog_fd < 0) {
		pr_warning("program '%s': can't attach BPF program w/o FD (did you load it?)\n",
			   bpf_program__title(prog, false));
		return ERR_PTR(-EINVAL);
	}

	link = malloc(sizeof(*link));
	if (!link)
		return ERR_PTR(-ENOMEM);
	link->link.destroy = &bpf_link__destroy_perf_event;
	link->fd = pfd;

	if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
		err = -errno;
		free(link);
		pr_warning("program '%s': failed to attach to pfd %d: %s\n",
			   bpf_program__title(prog, false), pfd,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return ERR_PTR(err);
	}
	if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
		err = -errno;
		free(link);
		pr_warning("program '%s': failed to enable pfd %d: %s\n",
			   bpf_program__title(prog, false), pfd,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return ERR_PTR(err);
	}
	return (struct bpf_link *)link;
}

/*
 * this function is expected to parse integer in the range of [0, 2^31-1] from
 * given file using scanf format string fmt. If actual parsed value is
 * negative, the result might be indistinguishable from error
 */
static int parse_uint_from_file(const char *file, const char *fmt)
{
	char buf[STRERR_BUFSIZE];
	int err, ret;
	FILE *f;

	f = fopen(file, "r");
	if (!f) {
		err = -errno;
		pr_debug("failed to open '%s': %s\n", file,
			 libbpf_strerror_r(err, buf, sizeof(buf)));
		return err;
	}
	err = fscanf(f, fmt, &ret);
	if (err != 1) {
		err = err == EOF ? -EIO : -errno;
		pr_debug("failed to parse '%s': %s\n", file,
			libbpf_strerror_r(err, buf, sizeof(buf)));
		fclose(f);
		return err;
	}
	fclose(f);
	return ret;
}

static int determine_kprobe_perf_type(void)
{
	const char *file = "/sys/bus/event_source/devices/kprobe/type";

	return parse_uint_from_file(file, "%d\n");
}

static int determine_uprobe_perf_type(void)
{
	const char *file = "/sys/bus/event_source/devices/uprobe/type";

	return parse_uint_from_file(file, "%d\n");
}

static int determine_kprobe_retprobe_bit(void)
{
	const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";

	return parse_uint_from_file(file, "config:%d\n");
}

static int determine_uprobe_retprobe_bit(void)
{
	const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";

	return parse_uint_from_file(file, "config:%d\n");
}

static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
				 uint64_t offset, int pid)
{
	struct perf_event_attr attr = {};
	char errmsg[STRERR_BUFSIZE];
	int type, pfd, err;

	type = uprobe ? determine_uprobe_perf_type()
		      : determine_kprobe_perf_type();
	if (type < 0) {
		pr_warning("failed to determine %s perf type: %s\n",
			   uprobe ? "uprobe" : "kprobe",
			   libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
		return type;
	}
	if (retprobe) {
		int bit = uprobe ? determine_uprobe_retprobe_bit()
				 : determine_kprobe_retprobe_bit();

		if (bit < 0) {
			pr_warning("failed to determine %s retprobe bit: %s\n",
				   uprobe ? "uprobe" : "kprobe",
				   libbpf_strerror_r(bit, errmsg,
						     sizeof(errmsg)));
			return bit;
		}
		attr.config |= 1 << bit;
	}
	attr.size = sizeof(attr);
	attr.type = type;
	attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */
	attr.config2 = offset;		       /* kprobe_addr or probe_offset */

	/* pid filter is meaningful only for uprobes */
	pfd = syscall(__NR_perf_event_open, &attr,
		      pid < 0 ? -1 : pid /* pid */,
		      pid == -1 ? 0 : -1 /* cpu */,
		      -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
	if (pfd < 0) {
		err = -errno;
		pr_warning("%s perf_event_open() failed: %s\n",
			   uprobe ? "uprobe" : "kprobe",
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return err;
	}
	return pfd;
}

struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
					    bool retprobe,
					    const char *func_name)
{
	char errmsg[STRERR_BUFSIZE];
	struct bpf_link *link;
	int pfd, err;

	pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
				    0 /* offset */, -1 /* pid */);
	if (pfd < 0) {
		pr_warning("program '%s': failed to create %s '%s' perf event: %s\n",
			   bpf_program__title(prog, false),
			   retprobe ? "kretprobe" : "kprobe", func_name,
			   libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
		return ERR_PTR(pfd);
	}
	link = bpf_program__attach_perf_event(prog, pfd);
	if (IS_ERR(link)) {
		close(pfd);
		err = PTR_ERR(link);
		pr_warning("program '%s': failed to attach to %s '%s': %s\n",
			   bpf_program__title(prog, false),
			   retprobe ? "kretprobe" : "kprobe", func_name,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return link;
	}
	return link;
}

struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
					    bool retprobe, pid_t pid,
					    const char *binary_path,
					    size_t func_offset)
{
	char errmsg[STRERR_BUFSIZE];
	struct bpf_link *link;
	int pfd, err;

	pfd = perf_event_open_probe(true /* uprobe */, retprobe,
				    binary_path, func_offset, pid);
	if (pfd < 0) {
		pr_warning("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
			   bpf_program__title(prog, false),
			   retprobe ? "uretprobe" : "uprobe",
			   binary_path, func_offset,
			   libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
		return ERR_PTR(pfd);
	}
	link = bpf_program__attach_perf_event(prog, pfd);
	if (IS_ERR(link)) {
		close(pfd);
		err = PTR_ERR(link);
		pr_warning("program '%s': failed to attach to %s '%s:0x%zx': %s\n",
			   bpf_program__title(prog, false),
			   retprobe ? "uretprobe" : "uprobe",
			   binary_path, func_offset,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return link;
	}
	return link;
}

static int determine_tracepoint_id(const char *tp_category,
				   const char *tp_name)
{
	char file[PATH_MAX];
	int ret;

	ret = snprintf(file, sizeof(file),
		       "/sys/kernel/debug/tracing/events/%s/%s/id",
		       tp_category, tp_name);
	if (ret < 0)
		return -errno;
	if (ret >= sizeof(file)) {
		pr_debug("tracepoint %s/%s path is too long\n",
			 tp_category, tp_name);
		return -E2BIG;
	}
	return parse_uint_from_file(file, "%d\n");
}

static int perf_event_open_tracepoint(const char *tp_category,
				      const char *tp_name)
{
	struct perf_event_attr attr = {};
	char errmsg[STRERR_BUFSIZE];
	int tp_id, pfd, err;

	tp_id = determine_tracepoint_id(tp_category, tp_name);
	if (tp_id < 0) {
		pr_warning("failed to determine tracepoint '%s/%s' perf event ID: %s\n",
			   tp_category, tp_name,
			   libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg)));
		return tp_id;
	}

	attr.type = PERF_TYPE_TRACEPOINT;
	attr.size = sizeof(attr);
	attr.config = tp_id;

	pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */,
		      -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
	if (pfd < 0) {
		err = -errno;
		pr_warning("tracepoint '%s/%s' perf_event_open() failed: %s\n",
			   tp_category, tp_name,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return err;
	}
	return pfd;
}

struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
						const char *tp_category,
						const char *tp_name)
{
	char errmsg[STRERR_BUFSIZE];
	struct bpf_link *link;
	int pfd, err;

	pfd = perf_event_open_tracepoint(tp_category, tp_name);
	if (pfd < 0) {
		pr_warning("program '%s': failed to create tracepoint '%s/%s' perf event: %s\n",
			   bpf_program__title(prog, false),
			   tp_category, tp_name,
			   libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
		return ERR_PTR(pfd);
	}
	link = bpf_program__attach_perf_event(prog, pfd);
	if (IS_ERR(link)) {
		close(pfd);
		err = PTR_ERR(link);
		pr_warning("program '%s': failed to attach to tracepoint '%s/%s': %s\n",
			   bpf_program__title(prog, false),
			   tp_category, tp_name,
			   libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
		return link;
	}
	return link;
}

static int bpf_link__destroy_fd(struct bpf_link *link)
{
	struct bpf_link_fd *l = (void *)link;

	return close(l->fd);
}

struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
						    const char *tp_name)
{
	char errmsg[STRERR_BUFSIZE];
	struct bpf_link_fd *link;
	int prog_fd, pfd;

	prog_fd = bpf_program__fd(prog);
	if (prog_fd < 0) {
		pr_warning("program '%s': can't attach before loaded\n",
			   bpf_program__title(prog, false));
		return ERR_PTR(-EINVAL);
	}

	link = malloc(sizeof(*link));
	if (!link)
		return ERR_PTR(-ENOMEM);
	link->link.destroy = &bpf_link__destroy_fd;

	pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
	if (pfd < 0) {
		pfd = -errno;
		free(link);
		pr_warning("program '%s': failed to attach to raw tracepoint '%s': %s\n",
			   bpf_program__title(prog, false), tp_name,
			   libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
		return ERR_PTR(pfd);
	}
	link->fd = pfd;
	return (struct bpf_link *)link;
}

enum bpf_perf_event_ret
enum bpf_perf_event_ret
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
			   void **copy_mem, size_t *copy_size,
			   void **copy_mem, size_t *copy_size,
+21 −0
Original line number Original line Diff line number Diff line
@@ -165,6 +165,27 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
LIBBPF_API void bpf_program__unload(struct bpf_program *prog);


struct bpf_link;

LIBBPF_API int bpf_link__destroy(struct bpf_link *link);

LIBBPF_API struct bpf_link *
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
LIBBPF_API struct bpf_link *
bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
			   const char *func_name);
LIBBPF_API struct bpf_link *
bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
			   pid_t pid, const char *binary_path,
			   size_t func_offset);
LIBBPF_API struct bpf_link *
bpf_program__attach_tracepoint(struct bpf_program *prog,
			       const char *tp_category,
			       const char *tp_name);
LIBBPF_API struct bpf_link *
bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
				   const char *tp_name);

struct bpf_insn;
struct bpf_insn;


/*
/*
+7 −1
Original line number Original line Diff line number Diff line
@@ -167,10 +167,16 @@ LIBBPF_0.0.3 {


LIBBPF_0.0.4 {
LIBBPF_0.0.4 {
	global:
	global:
		bpf_link__destroy;
		bpf_object__load_xattr;
		bpf_program__attach_kprobe;
		bpf_program__attach_perf_event;
		bpf_program__attach_raw_tracepoint;
		bpf_program__attach_tracepoint;
		bpf_program__attach_uprobe;
		btf_dump__dump_type;
		btf_dump__dump_type;
		btf_dump__free;
		btf_dump__free;
		btf_dump__new;
		btf_dump__new;
		btf__parse_elf;
		btf__parse_elf;
		bpf_object__load_xattr;
		libbpf_num_possible_cpus;
		libbpf_num_possible_cpus;
} LIBBPF_0.0.3;
} LIBBPF_0.0.3;
+1 −1
Original line number Original line Diff line number Diff line
@@ -11,7 +11,7 @@
 */
 */
char *libbpf_strerror_r(int err, char *dst, int len)
char *libbpf_strerror_r(int err, char *dst, int len)
{
{
	int ret = strerror_r(err, dst, len);
	int ret = strerror_r(err < 0 ? -err : err, dst, len);
	if (ret)
	if (ret)
		snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
		snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
	return dst;
	return dst;
+166 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>

ssize_t get_base_addr() {
	size_t start;
	char buf[256];
	FILE *f;

	f = fopen("/proc/self/maps", "r");
	if (!f)
		return -errno;

	while (fscanf(f, "%zx-%*x %s %*s\n", &start, buf) == 2) {
		if (strcmp(buf, "r-xp") == 0) {
			fclose(f);
			return start;
		}
	}

	fclose(f);
	return -EINVAL;
}

#ifdef __x86_64__
#define SYS_KPROBE_NAME "__x64_sys_nanosleep"
#else
#define SYS_KPROBE_NAME "sys_nanosleep"
#endif

void test_attach_probe(void)
{
	const char *kprobe_name = "kprobe/sys_nanosleep";
	const char *kretprobe_name = "kretprobe/sys_nanosleep";
	const char *uprobe_name = "uprobe/trigger_func";
	const char *uretprobe_name = "uretprobe/trigger_func";
	const int kprobe_idx = 0, kretprobe_idx = 1;
	const int uprobe_idx = 2, uretprobe_idx = 3;
	const char *file = "./test_attach_probe.o";
	struct bpf_program *kprobe_prog, *kretprobe_prog;
	struct bpf_program *uprobe_prog, *uretprobe_prog;
	struct bpf_object *obj;
	int err, prog_fd, duration = 0, res;
	struct bpf_link *kprobe_link = NULL;
	struct bpf_link *kretprobe_link = NULL;
	struct bpf_link *uprobe_link = NULL;
	struct bpf_link *uretprobe_link = NULL;
	int results_map_fd;
	size_t uprobe_offset;
	ssize_t base_addr;

	base_addr = get_base_addr();
	if (CHECK(base_addr < 0, "get_base_addr",
		  "failed to find base addr: %zd", base_addr))
		return;
	uprobe_offset = (size_t)&get_base_addr - base_addr;

	/* load programs */
	err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd);
	if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
		return;

	kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name);
	if (CHECK(!kprobe_prog, "find_probe",
		  "prog '%s' not found\n", kprobe_name))
		goto cleanup;
	kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name);
	if (CHECK(!kretprobe_prog, "find_probe",
		  "prog '%s' not found\n", kretprobe_name))
		goto cleanup;
	uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name);
	if (CHECK(!uprobe_prog, "find_probe",
		  "prog '%s' not found\n", uprobe_name))
		goto cleanup;
	uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name);
	if (CHECK(!uretprobe_prog, "find_probe",
		  "prog '%s' not found\n", uretprobe_name))
		goto cleanup;

	/* load maps */
	results_map_fd = bpf_find_map(__func__, obj, "results_map");
	if (CHECK(results_map_fd < 0, "find_results_map",
		  "err %d\n", results_map_fd))
		goto cleanup;

	kprobe_link = bpf_program__attach_kprobe(kprobe_prog,
						 false /* retprobe */,
						 SYS_KPROBE_NAME);
	if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
		  "err %ld\n", PTR_ERR(kprobe_link))) {
		kprobe_link = NULL;
		goto cleanup;
	}
	kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog,
						    true /* retprobe */,
						    SYS_KPROBE_NAME);
	if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe",
		  "err %ld\n", PTR_ERR(kretprobe_link))) {
		kretprobe_link = NULL;
		goto cleanup;
	}
	uprobe_link = bpf_program__attach_uprobe(uprobe_prog,
						 false /* retprobe */,
						 0 /* self pid */,
						 "/proc/self/exe",
						 uprobe_offset);
	if (CHECK(IS_ERR(uprobe_link), "attach_uprobe",
		  "err %ld\n", PTR_ERR(uprobe_link))) {
		uprobe_link = NULL;
		goto cleanup;
	}
	uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog,
						    true /* retprobe */,
						    -1 /* any pid */,
						    "/proc/self/exe",
						    uprobe_offset);
	if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe",
		  "err %ld\n", PTR_ERR(uretprobe_link))) {
		uretprobe_link = NULL;
		goto cleanup;
	}

	/* trigger & validate kprobe && kretprobe */
	usleep(1);

	err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res);
	if (CHECK(err, "get_kprobe_res",
		  "failed to get kprobe res: %d\n", err))
		goto cleanup;
	if (CHECK(res != kprobe_idx + 1, "check_kprobe_res",
		  "wrong kprobe res: %d\n", res))
		goto cleanup;

	err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res);
	if (CHECK(err, "get_kretprobe_res",
		  "failed to get kretprobe res: %d\n", err))
		goto cleanup;
	if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res",
		  "wrong kretprobe res: %d\n", res))
		goto cleanup;

	/* trigger & validate uprobe & uretprobe */
	get_base_addr();

	err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res);
	if (CHECK(err, "get_uprobe_res",
		  "failed to get uprobe res: %d\n", err))
		goto cleanup;
	if (CHECK(res != uprobe_idx + 1, "check_uprobe_res",
		  "wrong uprobe res: %d\n", res))
		goto cleanup;

	err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res);
	if (CHECK(err, "get_uretprobe_res",
		  "failed to get uretprobe res: %d\n", err))
		goto cleanup;
	if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res",
		  "wrong uretprobe res: %d\n", res))
		goto cleanup;

cleanup:
	bpf_link__destroy(kprobe_link);
	bpf_link__destroy(kretprobe_link);
	bpf_link__destroy(uprobe_link);
	bpf_link__destroy(uretprobe_link);
	bpf_object__close(obj);
}
Loading