Skip to content

Commit 5589e27

Browse files
mykyta5Kernel Patches Daemon
authored andcommitted
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 <[email protected]>
1 parent 27199db commit 5589e27

File tree

1 file changed

+74
-1
lines changed

1 file changed

+74
-1
lines changed

tools/testing/selftests/bpf/veristat.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ struct var_preset {
181181
bool applied;
182182
};
183183

184+
enum dump_mode {
185+
DUMP_NONE = 0,
186+
DUMP_XLATED = 1,
187+
DUMP_JITED = 2,
188+
};
189+
184190
static struct env {
185191
char **filenames;
186192
int filename_cnt;
@@ -227,6 +233,7 @@ static struct env {
227233
char orig_cgroup[PATH_MAX];
228234
char stat_cgroup[PATH_MAX];
229235
int memory_peak_fd;
236+
__u32 dump_mode;
230237
} env;
231238

232239
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
@@ -271,6 +278,7 @@ const char argp_program_doc[] =
271278
enum {
272279
OPT_LOG_FIXED = 1000,
273280
OPT_LOG_SIZE = 1001,
281+
OPT_DUMP = 1002,
274282
};
275283

276284
static const struct argp_option opts[] = {
@@ -295,10 +303,12 @@ static const struct argp_option opts[] = {
295303
"Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
296304
{ "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" },
297305
{ "set-global-vars", 'G', "GLOBAL", 0, "Set global variables provided in the expression, for example \"var1 = 1\"" },
306+
{ "dump", OPT_DUMP, "DUMP_MODE", OPTION_ARG_OPTIONAL, "Print BPF program dump (xlated, jited)" },
298307
{},
299308
};
300309

301310
static int parse_stats(const char *stats_str, struct stat_specs *specs);
311+
static int parse_dump_mode(char *mode_str, __u32 *dump_mode);
302312
static int append_filter(struct filter **filters, int *cnt, const char *str);
303313
static int append_filter_file(const char *path);
304314
static int append_var_preset(struct var_preset **presets, int *cnt, const char *expr);
@@ -427,6 +437,11 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
427437
return err;
428438
}
429439
break;
440+
case OPT_DUMP:
441+
err = parse_dump_mode(arg, &env.dump_mode);
442+
if (err)
443+
return err;
444+
break;
430445
default:
431446
return ARGP_ERR_UNKNOWN;
432447
}
@@ -956,6 +971,32 @@ static int parse_stats(const char *stats_str, struct stat_specs *specs)
956971
return 0;
957972
}
958973

974+
static int parse_dump_mode(char *mode_str, __u32 *dump_mode)
975+
{
976+
char *state = NULL, *cur;
977+
int cnt = 0;
978+
979+
if (!mode_str) {
980+
env.dump_mode = DUMP_XLATED;
981+
return 0;
982+
}
983+
984+
for (cur = mode_str; *cur; ++cur)
985+
*cur = tolower(*cur);
986+
987+
while ((cur = strtok_r(cnt++ ? NULL : mode_str, ",", &state))) {
988+
if (strcmp(cur, "jited") == 0) {
989+
env.dump_mode |= DUMP_JITED;
990+
} else if (strcmp(cur, "xlated") == 0) {
991+
env.dump_mode |= DUMP_XLATED;
992+
} else {
993+
fprintf(stderr, "Unrecognized dump mode '%s'\n", cur);
994+
return -EINVAL;
995+
}
996+
}
997+
return 0;
998+
}
999+
9591000
static void free_verif_stats(struct verif_stats *stats, size_t stat_cnt)
9601001
{
9611002
int i;
@@ -1554,6 +1595,35 @@ static int parse_rvalue(const char *val, struct rvalue *rvalue)
15541595
return 0;
15551596
}
15561597

1598+
static void dump(__u32 prog_id, const char *file_name, const char *prog_name)
1599+
{
1600+
char command[64], buf[1024];
1601+
enum dump_mode modes[2] = { DUMP_XLATED, DUMP_JITED };
1602+
const char *mode_lower[2] = { "xlated", "jited" };
1603+
const char *mode_upper[2] = { "XLATED", "JITED" };
1604+
FILE *fp;
1605+
int i;
1606+
1607+
for (i = 0; i < ARRAY_SIZE(modes); ++i) {
1608+
if (!(env.dump_mode & modes[i]))
1609+
continue;
1610+
snprintf(command, sizeof(command), "bpftool prog dump %s id %u",
1611+
mode_lower[i], prog_id);
1612+
1613+
fp = popen(command, "r");
1614+
if (!fp) {
1615+
fprintf(stderr, "Can't run bpftool\n");
1616+
return;
1617+
}
1618+
1619+
printf("%s/%s DUMP %s:\n", file_name, prog_name, mode_upper[i]);
1620+
while (fgets(buf, sizeof(buf), fp))
1621+
printf("%s", buf);
1622+
printf("\n");
1623+
pclose(fp);
1624+
}
1625+
}
1626+
15571627
static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
15581628
{
15591629
const char *base_filename = basename(strdupa(filename));
@@ -1630,8 +1700,11 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
16301700

16311701
memset(&info, 0, info_len);
16321702
fd = bpf_program__fd(prog);
1633-
if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
1703+
if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) {
16341704
stats->stats[JITED_SIZE] = info.jited_prog_len;
1705+
if (env.dump_mode != DUMP_NONE)
1706+
dump(info.id, base_filename, prog_name);
1707+
}
16351708

16361709
parse_verif_log(buf, buf_sz, stats);
16371710

0 commit comments

Comments
 (0)