This patch introduces minimum support in bpftool to dump and format the contents of inner oracle maps. This new "bpftool oracle dump" command is only meant to help demo and debug previous commits and is at the very least missing support for JSON output. The current output looks like: # ./bpftool oracle dump id 22 State 0: R0=scalar(u64=[0; 18446744073709551615], s64=[-9223372036854775808; 9223372036854775807], u32=[0; 4294967295], s32=[-2147483648; 2147483647], var_off=(0; 0xffffffffffffffff) R6=scalar(u64=[4294967252; 4294967252], s64=[4294967252; 4294967252], u32=[4294967252; 4294967252], s32=[-44; -44], var_off=(0xffffffd4; 0) Found 1 state Signed-off-by: Paul Chaignon --- tools/bpf/bpftool/main.c | 3 +- tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/oracle.c | 154 +++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 10 +++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 tools/bpf/bpftool/oracle.c diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index a829a6a49037..c4101d7ae965 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -64,7 +64,7 @@ static int do_help(int argc, char **argv) " %s batch file FILE\n" " %s version\n" "\n" - " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter | token }\n" + " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter | token | oracle }\n" " " HELP_SPEC_OPTIONS " |\n" " {-V|--version} }\n" "", @@ -81,6 +81,7 @@ static const struct cmd commands[] = { { "batch", do_batch }, { "prog", do_prog }, { "map", do_map }, + { "oracle", do_oracle }, { "link", do_link }, { "cgroup", do_cgroup }, { "perf", do_perf }, diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 1130299cede0..9ee613d351a4 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -166,6 +166,7 @@ int do_btf(int argc, char **argv); /* non-bootstrap only commands */ int do_prog(int argc, char **arg) __weak; int do_map(int argc, char **arg) __weak; +int do_oracle(int argc, char **arg) __weak; int do_link(int argc, char **arg) __weak; int do_event_pipe(int argc, char **argv) __weak; int do_cgroup(int argc, char **arg) __weak; diff --git a/tools/bpf/bpftool/oracle.c b/tools/bpf/bpftool/oracle.c new file mode 100644 index 000000000000..c0a518ff5ee2 --- /dev/null +++ b/tools/bpf/bpftool/oracle.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include "main.h" + +struct tnum { + __u64 value; + __u64 mask; +}; + +struct bpf_reg_oracle_state { + bool scalar; + bool ptr_not_null; + + struct tnum var_off; + __s64 smin_value; + __s64 smax_value; + __u64 umin_value; + __u64 umax_value; + __s32 s32_min_value; + __s32 s32_max_value; + __u32 u32_min_value; + __u32 u32_max_value; +}; + +struct bpf_oracle_state { + struct bpf_reg_oracle_state regs[MAX_BPF_REG - 1]; +}; + +static void print_register_state(int i, struct bpf_reg_oracle_state *reg) +{ + if (!reg->scalar && !reg->ptr_not_null) + return; + + printf("R%d=", i); + if (reg->scalar) { + printf("scalar(u64=[%llu; %llu], s64=[%lld; %lld], u32=[%u; %u], s32=[%d; %d]", + reg->umin_value, reg->umax_value, reg->smin_value, reg->smax_value, + reg->u32_min_value, reg->u32_max_value, reg->s32_min_value, + reg->s32_max_value); + printf(", var_off=(%#llx; %#llx)", reg->var_off.value, reg->var_off.mask); + } else if (reg->ptr_not_null) { + printf("ptr"); + } else { + printf("unknown"); + } + printf("\n"); +} + +static int +oracle_map_dump(int fd, struct bpf_map_info *info, bool show_header) +{ + struct bpf_oracle_state value = {}; + unsigned int num_elems = 0; + __u32 key, *prev_key = NULL; + int err, i; + + while (true) { + err = bpf_map_get_next_key(fd, prev_key, &key); + if (err) { + if (errno == ENOENT) + err = 0; + break; + } + if (bpf_map_lookup_elem(fd, &key, &value)) { + printf(""); + continue; + } + printf("State %u:\n", key); + for (i = 0; i < MAX_BPF_REG - 1; i++) + print_register_state(i, &value.regs[i]); + printf("\n"); + num_elems++; + prev_key = &key; + } + + printf("Found %u state%s\n", num_elems, + num_elems != 1 ? "s" : ""); + + close(fd); + return err; +} + +static int do_dump(int argc, char **argv) +{ + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + int nb_fds, i, err; + int *fds = NULL; + + fds = malloc(sizeof(int)); + if (!fds) { + p_err("mem alloc failed"); + return -1; + } + nb_fds = map_parse_fds(&argc, &argv, &fds, BPF_F_RDONLY); + if (nb_fds < 1) + goto exit_free; + + for (i = 0; i < nb_fds; i++) { + if (bpf_map_get_info_by_fd(fds[i], &info, &len)) { + p_err("can't get map info: %s", strerror(errno)); + break; + } + if (info.type != BPF_MAP_TYPE_ARRAY || info.key_size != sizeof(__u32) || + info.value_size != sizeof(struct bpf_oracle_state)) { + p_err("not an oracle map"); + break; + } + err = oracle_map_dump(fds[i], &info, nb_fds > 1); + if (i != nb_fds - 1) + printf("\n"); + + if (err) + break; + close(fds[i]); + } + + for (; i < nb_fds; i++) + close(fds[i]); +exit_free: + free(fds); + return 0; +} + +static int do_help(int argc, char **argv) +{ + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + + fprintf(stderr, + "Usage: %1$s %2$s dump MAP\n" + " %1$s %2$s help\n" + "\n" + " " HELP_SPEC_MAP "\n" + " " HELP_SPEC_OPTIONS " |\n" + " {-f|--bpffs} | {-n|--nomount} }\n" + "", + bin_name, argv[-2]); + + return 0; +} + +static const struct cmd cmds[] = { + { "help", do_help }, + { "dump", do_dump }, + { 0 } +}; + +int do_oracle(int argc, char **argv) +{ + return cmd_select(cmds, argc, argv, do_help); +} diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 6b92b0847ec2..f19dba37ea7d 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1345,6 +1345,16 @@ enum { #define BPF_PSEUDO_MAP_VALUE 2 #define BPF_PSEUDO_MAP_IDX_VALUE 6 +/* Internal only. + * insn[0].dst_reg: 0 + * insn[0].src_reg: BPF_PSEUDO_MAP_ORACLE + * insn[0].imm: address of oracle state list + * insn[1].imm: address of oracle state list + * insn[0].off: 0 + * insn[1].off: 0 + */ +#define BPF_PSEUDO_MAP_ORACLE 7 + /* insn[0].src_reg: BPF_PSEUDO_BTF_ID * insn[0].imm: kernel btd id of VAR * insn[1].imm: 0 -- 2.43.0