Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
4e9e0760 b4406e10

+94 -15
+3
tools/testing/selftests/bpf/test_progs.c
··· 35 35 dprintf(fd, "<backtrace not supported>\n"); 36 36 } 37 37 38 + int env_verbosity = 0; 39 + 38 40 static bool verbose(void) 39 41 { 40 42 return env.verbosity > VERBOSE_NONE; ··· 975 973 return -EINVAL; 976 974 } 977 975 } 976 + env_verbosity = env->verbosity; 978 977 979 978 if (verbose()) { 980 979 if (setenv("SELFTESTS_VERBOSE", "1", 1) == -1) {
+2
tools/testing/selftests/bpf/test_progs.h
··· 96 96 FILE *stdout_saved; 97 97 }; 98 98 99 + extern int env_verbosity; 100 + 99 101 struct test_env { 100 102 struct test_selector test_selector; 101 103 struct test_selector subtest_selector;
+89 -15
tools/testing/selftests/bpf/trace_helpers.c
··· 10 10 #include <pthread.h> 11 11 #include <unistd.h> 12 12 #include <linux/perf_event.h> 13 + #include <linux/fs.h> 14 + #include <sys/ioctl.h> 13 15 #include <sys/mman.h> 14 16 #include "trace_helpers.h" 15 17 #include <linux/limits.h> ··· 246 244 return err; 247 245 } 248 246 247 + #ifdef PROCMAP_QUERY 248 + int env_verbosity __weak = 0; 249 + 250 + int procmap_query(int fd, const void *addr, __u32 query_flags, size_t *start, size_t *offset, int *flags) 251 + { 252 + char path_buf[PATH_MAX], build_id_buf[20]; 253 + struct procmap_query q; 254 + int err; 255 + 256 + memset(&q, 0, sizeof(q)); 257 + q.size = sizeof(q); 258 + q.query_flags = query_flags; 259 + q.query_addr = (__u64)addr; 260 + q.vma_name_addr = (__u64)path_buf; 261 + q.vma_name_size = sizeof(path_buf); 262 + q.build_id_addr = (__u64)build_id_buf; 263 + q.build_id_size = sizeof(build_id_buf); 264 + 265 + err = ioctl(fd, PROCMAP_QUERY, &q); 266 + if (err < 0) { 267 + err = -errno; 268 + if (err == -ENOTTY) 269 + return -EOPNOTSUPP; /* ioctl() not implemented yet */ 270 + if (err == -ENOENT) 271 + return -ESRCH; /* vma not found */ 272 + return err; 273 + } 274 + 275 + if (env_verbosity >= 1) { 276 + printf("VMA FOUND (addr %08lx): %08lx-%08lx %c%c%c%c %08lx %02x:%02x %ld %s (build ID: %s, %d bytes)\n", 277 + (long)addr, (long)q.vma_start, (long)q.vma_end, 278 + (q.vma_flags & PROCMAP_QUERY_VMA_READABLE) ? 'r' : '-', 279 + (q.vma_flags & PROCMAP_QUERY_VMA_WRITABLE) ? 'w' : '-', 280 + (q.vma_flags & PROCMAP_QUERY_VMA_EXECUTABLE) ? 'x' : '-', 281 + (q.vma_flags & PROCMAP_QUERY_VMA_SHARED) ? 's' : 'p', 282 + (long)q.vma_offset, q.dev_major, q.dev_minor, (long)q.inode, 283 + q.vma_name_size ? path_buf : "", 284 + q.build_id_size ? "YES" : "NO", 285 + q.build_id_size); 286 + } 287 + 288 + *start = q.vma_start; 289 + *offset = q.vma_offset; 290 + *flags = q.vma_flags; 291 + return 0; 292 + } 293 + #else 294 + int procmap_query(int fd, const void *addr, size_t *start, size_t *offset, int *flags) 295 + { 296 + return -EOPNOTSUPP; 297 + } 298 + #endif 299 + 249 300 ssize_t get_uprobe_offset(const void *addr) 250 301 { 251 - size_t start, end, base; 252 - char buf[256]; 253 - bool found = false; 302 + size_t start, base, end; 254 303 FILE *f; 304 + char buf[256]; 305 + int err, flags; 255 306 256 307 f = fopen("/proc/self/maps", "r"); 257 308 if (!f) 258 309 return -errno; 259 310 260 - while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { 261 - if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { 262 - found = true; 263 - break; 311 + /* requested executable VMA only */ 312 + err = procmap_query(fileno(f), addr, PROCMAP_QUERY_VMA_EXECUTABLE, &start, &base, &flags); 313 + if (err == -EOPNOTSUPP) { 314 + bool found = false; 315 + 316 + while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) { 317 + if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) { 318 + found = true; 319 + break; 320 + } 264 321 } 322 + if (!found) { 323 + fclose(f); 324 + return -ESRCH; 325 + } 326 + } else if (err) { 327 + fclose(f); 328 + return err; 265 329 } 266 - 267 330 fclose(f); 268 - 269 - if (!found) 270 - return -ESRCH; 271 331 272 332 #if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2 273 333 ··· 371 307 size_t start, end, offset; 372 308 char buf[256]; 373 309 FILE *f; 310 + int err, flags; 374 311 375 312 f = fopen("/proc/self/maps", "r"); 376 313 if (!f) 377 314 return -errno; 378 315 379 - while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 380 - if (addr >= start && addr < end) { 381 - fclose(f); 382 - return (size_t)addr - start + offset; 316 + err = procmap_query(fileno(f), (const void *)addr, 0, &start, &offset, &flags); 317 + if (err == 0) { 318 + fclose(f); 319 + return (size_t)addr - start + offset; 320 + } else if (err != -EOPNOTSUPP) { 321 + fclose(f); 322 + return err; 323 + } else if (err) { 324 + while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &offset) == 4) { 325 + if (addr >= start && addr < end) { 326 + fclose(f); 327 + return (size_t)addr - start + offset; 328 + } 383 329 } 384 330 } 385 331