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: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20240806230319.869734-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Andrii Nakryiko 2024-08-06 16:03:19 -07:00 committed by Alexei Starovoitov
parent b4406e1003
commit 4e9e07603e
3 changed files with 94 additions and 15 deletions

View File

@ -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) {

View File

@ -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;

View File

@ -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,29 +246,91 @@ 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;
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;
/* 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);
return -ESRCH;
}
} else if (err) {
fclose(f);
return err;
}
fclose(f);
if (!found)
return -ESRCH;
#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2
#define OP_RT_RA_MASK 0xffff0000UL
@ -307,15 +371,25 @@ 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;
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;
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;
}
}
}