Commit 4e9e0760 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov
Browse files

selftests/bpf: make use of PROCMAP_QUERY ioctl if available



Instead of parsing text-based /proc/<pid>/maps file, try to use
PROCMAP_QUERY ioctl() to simplify and speed up data fetching.
This logic is used to do uprobe file offset calculation, so any bugs in
this logic would manifest as failing uprobe BPF selftests.

This also serves as a simple demonstration of one of the intended uses.

Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20240806230319.869734-1-andrii@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent b4406e10
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ __weak void backtrace_symbols_fd(void *const *buffer, int size, int fd)
	dprintf(fd, "<backtrace not supported>\n");
}

int env_verbosity = 0;

static bool verbose(void)
{
	return env.verbosity > VERBOSE_NONE;
@@ -973,6 +975,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
				return -EINVAL;
			}
		}
		env_verbosity = env->verbosity;

		if (verbose()) {
			if (setenv("SELFTESTS_VERBOSE", "1", 1) == -1) {
+2 −0
Original line number Diff line number Diff line
@@ -96,6 +96,8 @@ struct test_state {
	FILE *stdout_saved;
};

extern int env_verbosity;

struct test_env {
	struct test_selector test_selector;
	struct test_selector subtest_selector;
+89 −15
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@
#include <pthread.h>
#include <unistd.h>
#include <linux/perf_event.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "trace_helpers.h"
#include <linux/limits.h>
@@ -244,28 +246,90 @@ int kallsyms_find(const char *sym, unsigned long long *addr)
	return err;
}

#ifdef PROCMAP_QUERY
int env_verbosity __weak = 0;

int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags)
{
	char path_buf[PATH_MAX], build_id_buf[20];
	struct procmap_query q;
	int err;

	memset(&q, 0, sizeof(q));
	q.size = sizeof(q);
	q.query_flags = query_flags;
	q.query_addr = (__u64)addr;
	q.vma_name_addr = (__u64)path_buf;
	q.vma_name_size = sizeof(path_buf);
	q.build_id_addr = (__u64)build_id_buf;
	q.build_id_size = sizeof(build_id_buf);

	err = ioctl(fd, PROCMAP_QUERY, &q);
	if (err < 0) {
		err = -errno;
		if (err == -ENOTTY)
			return -EOPNOTSUPP; /* ioctl() not implemented yet */
		if (err == -ENOENT)
			return -ESRCH; /* vma not found */
		return err;
	}

	if (env_verbosity >= 1) {
		printf("VMA FOUND (addr %08lx): %08lx-%08lx %c%c%c%c %08lx %02x:%02x %ld %s (build ID: %s, %d bytes)\n",
		       (long)addr, (long)q.vma_start, (long)q.vma_end,
		       (q.vma_flags & PROCMAP_QUERY_VMA_READABLE) ? 'r' : '-',
		       (q.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) ? 'w' : '-',
		       (q.vma_flags & PROCMAP_QUERY_VMA_EXECUTABLE) ? 'x' : '-',
		       (q.vma_flags & PROCMAP_QUERY_VMA_SHARED) ? 's' : 'p',
		       (long)q.vma_offset, q.dev_major, q.dev_minor, (long)q.inode,
		       q.vma_name_size ? path_buf : "",
		       q.build_id_size ? "YES" : "NO",
		       q.build_id_size);
	}

	*start = q.vma_start;
	*offset = q.vma_offset;
	*flags = q.vma_flags;
	return 0;
}
#else
int procmap_query(int fd, const void *addr, size_t *start, size_t *offset, int *flags)
{
	return -EOPNOTSUPP;
}
#endif

ssize_t get_uprobe_offset(const void *addr)
{
	size_t start, end, base;
	char buf[256];
	bool found = false;
	size_t start, base, end;
	FILE *f;
	char buf[256];
	int err, flags;

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

	/* requested executable VMA only */
	err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE, &start, &base, &flags);
	if (err == -EOPNOTSUPP) {
		bool found = false;

		while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
			if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
				found = true;
				break;
			}
		}

		if (!found) {
			fclose(f);

	if (!found)
			return -ESRCH;
		}
	} else if (err) {
		fclose(f);
		return err;
	}
	fclose(f);

#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2

@@ -307,17 +371,27 @@ ssize_t get_rel_offset(uintptr_t addr)
	size_t start, end, offset;
	char buf[256];
	FILE *f;
	int err, flags;

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

	err = procmap_query(fileno(f), (const void *)addr, 0, &start, &offset, &flags);
	if (err == 0) {
		fclose(f);
		return (size_t)addr - start + offset;
	} else if (err != -EOPNOTSUPP) {
		fclose(f);
		return err;
	} else if (err) {
		while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) {
			if (addr >= start && addr < end) {
				fclose(f);
				return (size_t)addr - start + offset;
			}
		}
	}

	fclose(f);
	return -EINVAL;