Re: [PATCH bpf-next v4] selftests/bpf: add BPF program dump in veristat

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 29, 2025 at 6:28 AM Mykyta Yatsenko
<mykyta.yatsenko5@xxxxxxxxx> wrote:
>
> From: Mykyta Yatsenko <yatsenko@xxxxxxxx>
>
> 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@xxxxxxxx>
> ---
>  tools/testing/selftests/bpf/veristat.c | 75 +++++++++++++++++++++++++-
>  1 file changed, 74 insertions(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
> index d532dd82a3a8..c824f6e72e2f 100644
> --- a/tools/testing/selftests/bpf/veristat.c
> +++ b/tools/testing/selftests/bpf/veristat.c
> @@ -181,6 +181,12 @@ struct var_preset {
>         bool applied;
>  };
>
> +enum dump_mode {
> +       DUMP_NONE = 0,
> +       DUMP_XLATED = 1,
> +       DUMP_JITED = 2,
> +};
> +
>  static struct env {
>         char **filenames;
>         int filename_cnt;
> @@ -227,6 +233,7 @@ static struct env {
>         char orig_cgroup[PATH_MAX];
>         char stat_cgroup[PATH_MAX];
>         int memory_peak_fd;
> +       __u32 dump_mode;
>  } env;
>
>  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[] =
>  enum {
>         OPT_LOG_FIXED = 1000,
>         OPT_LOG_SIZE = 1001,
> +       OPT_DUMP = 1002,
>  };
>
>  static const struct argp_option opts[] = {
> @@ -295,10 +303,12 @@ static const struct argp_option opts[] = {
>           "Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
>         { "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" },
>         { "set-global-vars", 'G', "GLOBAL", 0, "Set global variables provided in the expression, for example \"var1 = 1\"" },
> +       { "dump", OPT_DUMP, "DUMP_MODE", OPTION_ARG_OPTIONAL, "Print BPF program dump (xlated, jited)" },
>         {},
>  };
>
>  static int parse_stats(const char *stats_str, struct stat_specs *specs);
> +static int parse_dump_mode(char *mode_str, __u32 *dump_mode);
>  static int append_filter(struct filter **filters, int *cnt, const char *str);
>  static int append_filter_file(const char *path);
>  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)
>                         return err;
>                 }
>                 break;
> +       case OPT_DUMP:
> +               err = parse_dump_mode(arg, &env.dump_mode);
> +               if (err)
> +                       return err;
> +               break;
>         default:
>                 return ARGP_ERR_UNKNOWN;
>         }
> @@ -956,6 +971,32 @@ static int parse_stats(const char *stats_str, struct stat_specs *specs)
>         return 0;
>  }
>
> +static int parse_dump_mode(char *mode_str, __u32 *dump_mode)
> +{
> +       char *state = NULL, *cur;
> +       int cnt = 0;
> +
> +       if (!mode_str) {
> +               env.dump_mode = DUMP_XLATED;
> +               return 0;
> +       }
> +
> +       for (cur = mode_str; *cur; ++cur)
> +               *cur = tolower(*cur);
> +
> +       while ((cur = strtok_r(cnt++ ? NULL : mode_str, ",", &state))) {

there is no need to parse comma-separated list, it is allowed to
specify the same CLI flag multiple times, so we can use that to
accumulate multiple modes:

--dump=xlated --dump=jited


this will do the trick, so keep it simple.

And then you don't need to do tolower(), you can just strcasecmp() against arg

pw-bot: cr

> +               if (strcmp(cur, "jited") == 0) {
> +                       env.dump_mode |= DUMP_JITED;
> +               } else if (strcmp(cur, "xlated") == 0) {
> +                       env.dump_mode |= DUMP_XLATED;
> +               } else {
> +                       fprintf(stderr, "Unrecognized dump mode '%s'\n", cur);
> +                       return -EINVAL;
> +               }
> +       }
> +       return 0;
> +}
> +
>  static void free_verif_stats(struct verif_stats *stats, size_t stat_cnt)
>  {
>         int i;
> @@ -1554,6 +1595,35 @@ static int parse_rvalue(const char *val, struct rvalue *rvalue)
>         return 0;
>  }
>
> +static void dump(__u32 prog_id, const char *file_name, const char *prog_name)
> +{
> +       char command[64], buf[1024];
> +       enum dump_mode modes[2] = { DUMP_XLATED, DUMP_JITED };
> +       const char *mode_lower[2] = { "xlated", "jited" };
> +       const char *mode_upper[2] = { "XLATED", "JITED" };

Instead of this, why don't we just pass desired mode as an argument,
and then just call dump(DUMP_XLATED) if env.dump_mode & DUMP_XLATED,
and similarly for DUMP_JITED. It's not like we'll have tons of
different dump modes, right?

> +       FILE *fp;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(modes); ++i) {
> +               if (!(env.dump_mode & modes[i]))
> +                       continue;
> +               snprintf(command, sizeof(command), "bpftool prog dump %s id %u",
> +                        mode_lower[i], prog_id);

should we check if bpftool is installed first? I.e., run `which
bpftool` and check if that's successful? And if not, print some
user-friendly warning or error?

> +
> +               fp = popen(command, "r");
> +               if (!fp) {
> +                       fprintf(stderr, "Can't run bpftool\n");
> +                       return;
> +               }
> +
> +               printf("%s/%s DUMP %s:\n", file_name, prog_name, mode_upper[i]);
> +               while (fgets(buf, sizeof(buf), fp))
> +                       printf("%s", buf);

nit: is there any advantage to using fgets+printf vs just binary-based
read/write? (and I'd probably make buf page sized)

> +               printf("\n");
> +               pclose(fp);
> +       }
> +}
> +
>  static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
>  {
>         const char *base_filename = basename(strdupa(filename));
> @@ -1630,8 +1700,11 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>
>         memset(&info, 0, info_len);
>         fd = bpf_program__fd(prog);
> -       if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0)
> +       if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) {
>                 stats->stats[JITED_SIZE] = info.jited_prog_len;
> +               if (env.dump_mode != DUMP_NONE)
> +                       dump(info.id, base_filename, prog_name);
> +       }
>
>         parse_verif_log(buf, buf_sz, stats);
>
> --
> 2.51.0
>





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux