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

selftests/bpf: Add BPF program dump in veristat

Add the ability to dump BPF program instructions directly from veristat.
Previously, inspecting a program required separate bpftool invocations:
one to load and another to dump it, which meant running multiple
commands.
During active development, it's common for developers to use veristat
for testing verification. Integrating instruction dumping into veristat
reduces the need to switch tools and simplifies the workflow.
By making this information more readily accessible, this change aims
to streamline the BPF development cycle and improve usability for
developers.
This implementation leverages bpftool, by running it directly via popen
to avoid any code duplication and keep veristat simple.

Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250905140835.1416179-1-mykyta.yatsenko5@gmail.com

authored by

Mykyta Yatsenko and committed by
Andrii Nakryiko
e12873ee 9621eb63

+55 -1
+55 -1
tools/testing/selftests/bpf/veristat.c
··· 181 181 bool applied; 182 182 }; 183 183 184 + enum dump_mode { 185 + DUMP_NONE = 0, 186 + DUMP_XLATED = 1, 187 + DUMP_JITED = 2, 188 + }; 189 + 184 190 static struct env { 185 191 char **filenames; 186 192 int filename_cnt; ··· 233 227 char orig_cgroup[PATH_MAX]; 234 228 char stat_cgroup[PATH_MAX]; 235 229 int memory_peak_fd; 230 + __u32 dump_mode; 236 231 } env; 237 232 238 233 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) ··· 278 271 enum { 279 272 OPT_LOG_FIXED = 1000, 280 273 OPT_LOG_SIZE = 1001, 274 + OPT_DUMP = 1002, 281 275 }; 282 276 283 277 static const struct argp_option opts[] = { ··· 303 295 "Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" }, 304 296 { "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" }, 305 297 { "set-global-vars", 'G', "GLOBAL", 0, "Set global variables provided in the expression, for example \"var1 = 1\"" }, 298 + { "dump", OPT_DUMP, "DUMP_MODE", OPTION_ARG_OPTIONAL, "Print BPF program dump (xlated, jited)" }, 306 299 {}, 307 300 }; 308 301 ··· 434 425 if (err) { 435 426 fprintf(stderr, "Failed to collect BPF object files: %d\n", err); 436 427 return err; 428 + } 429 + break; 430 + case OPT_DUMP: 431 + if (!arg || strcasecmp(arg, "xlated") == 0) { 432 + env.dump_mode |= DUMP_XLATED; 433 + } else if (strcasecmp(arg, "jited") == 0) { 434 + env.dump_mode |= DUMP_JITED; 435 + } else { 436 + fprintf(stderr, "Unrecognized dump mode '%s'\n", arg); 437 + return -EINVAL; 437 438 } 438 439 break; 439 440 default: ··· 1573 1554 return 0; 1574 1555 } 1575 1556 1557 + static void dump(__u32 prog_id, enum dump_mode mode, const char *file_name, const char *prog_name) 1558 + { 1559 + char command[64], buf[4096]; 1560 + FILE *fp; 1561 + int status; 1562 + 1563 + status = system("command -v bpftool > /dev/null 2>&1"); 1564 + if (status != 0) { 1565 + fprintf(stderr, "bpftool is not available, can't print program dump\n"); 1566 + return; 1567 + } 1568 + snprintf(command, sizeof(command), "bpftool prog dump %s id %u", 1569 + mode == DUMP_JITED ? "jited" : "xlated", prog_id); 1570 + fp = popen(command, "r"); 1571 + if (!fp) { 1572 + fprintf(stderr, "bpftool failed with error: %d\n", errno); 1573 + return; 1574 + } 1575 + 1576 + printf("DUMP (%s) %s/%s:\n", mode == DUMP_JITED ? "JITED" : "XLATED", file_name, prog_name); 1577 + while (fgets(buf, sizeof(buf), fp)) 1578 + fputs(buf, stdout); 1579 + fprintf(stdout, "\n"); 1580 + 1581 + if (ferror(fp)) 1582 + fprintf(stderr, "Failed to dump BPF prog with error: %d\n", errno); 1583 + 1584 + pclose(fp); 1585 + } 1586 + 1576 1587 static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog) 1577 1588 { 1578 1589 const char *base_filename = basename(strdupa(filename)); ··· 1679 1630 1680 1631 memset(&info, 0, info_len); 1681 1632 fd = bpf_program__fd(prog); 1682 - if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) 1633 + if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) { 1683 1634 stats->stats[JITED_SIZE] = info.jited_prog_len; 1635 + if (env.dump_mode & DUMP_JITED) 1636 + dump(info.id, DUMP_JITED, base_filename, prog_name); 1637 + if (env.dump_mode & DUMP_XLATED) 1638 + dump(info.id, DUMP_XLATED, base_filename, prog_name); 1639 + } 1684 1640 1685 1641 parse_verif_log(buf, buf_sz, stats); 1686 1642