Message ID | 20210913181510.46058-6-stephen@networkplumber.org (mailing list archive) |
---|---|
State | Superseded, archived |
Delegated to: | Thomas Monjalon |
Headers | show |
Series | Packet capture framework enhancements | expand |
Context | Check | Description |
---|---|---|
ci/checkpatch | success | coding style OK |
> When debugging converted (and other) programs it is useful > to see disassembled eBPF output. > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> > --- > lib/bpf/bpf_convert.c | 7 ++- > lib/bpf/bpf_dump.c | 118 ++++++++++++++++++++++++++++++++++++++++++ > lib/bpf/meson.build | 1 + > lib/bpf/rte_bpf.h | 14 +++++ > lib/bpf/version.map | 1 + > 5 files changed, 140 insertions(+), 1 deletion(-) > create mode 100644 lib/bpf/bpf_dump.c > > diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c > index a46ffeb067dd..db84add7dcce 100644 > --- a/lib/bpf/bpf_convert.c > +++ b/lib/bpf/bpf_convert.c > @@ -331,7 +331,12 @@ static int bpf_convert_filter(const struct bpf_insn *prog, size_t len, > case BPF_LD | BPF_IND | BPF_H: > case BPF_LD | BPF_IND | BPF_B: > /* All arithmetic insns map as-is. */ > - *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k); > + insn->code = fp->code; > + insn->dst_reg = BPF_REG_A; > + bpf_src = BPF_SRC(fp->code); > + insn->src_reg = bpf_src == BPF_X ? BPF_REG_X : 0; > + insn->off = 0; > + insn->imm = fp->k; > break; Should it be part of that patch? Looks like belongs to previous one, no? > > /* Jump transformation cannot use BPF block macros > diff --git a/lib/bpf/bpf_dump.c b/lib/bpf/bpf_dump.c > new file mode 100644 > index 000000000000..a6a431e64903 > --- /dev/null > +++ b/lib/bpf/bpf_dump.c > @@ -0,0 +1,118 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright (c) 2021 Stephen Hemminger > + * Based on filter2xdp > + * Copyright (C) 2017 Tobias Klauser > + */ > + > +#include <stdio.h> > +#include <stdint.h> > + > +#include "rte_bpf.h" > + > +#define BPF_OP_INDEX(x) (BPF_OP(x) >> 4) > +#define BPF_SIZE_INDEX(x) (BPF_SIZE(x) >> 3) > + > +static const char *const class_tbl[] = { > + [BPF_LD] = "ld", [BPF_LDX] = "ldx", [BPF_ST] = "st", > + [BPF_STX] = "stx", [BPF_ALU] = "alu", [BPF_JMP] = "jmp", > + [BPF_RET] = "ret", [BPF_MISC] = "alu64", > +}; > + > +static const char *const alu_op_tbl[16] = { > + [BPF_ADD >> 4] = "add", [BPF_SUB >> 4] = "sub", > + [BPF_MUL >> 4] = "mul", [BPF_DIV >> 4] = "div", > + [BPF_OR >> 4] = "or", [BPF_AND >> 4] = "and", > + [BPF_LSH >> 4] = "lsh", [BPF_RSH >> 4] = "rsh", > + [BPF_NEG >> 4] = "neg", [BPF_MOD >> 4] = "mod", > + [BPF_XOR >> 4] = "xor", [EBPF_MOV >> 4] = "mov", > + [EBPF_ARSH >> 4] = "arsh", [EBPF_END >> 4] = "endian", > +}; > + > +static const char *const size_tbl[] = { > + [BPF_W >> 3] = "w", > + [BPF_H >> 3] = "h", > + [BPF_B >> 3] = "b", > + [EBPF_DW >> 3] = "dw", > +}; > + > +static const char *const jump_tbl[16] = { > + [BPF_JA >> 4] = "ja", [BPF_JEQ >> 4] = "jeq", > + [BPF_JGT >> 4] = "jgt", [BPF_JGE >> 4] = "jge", > + [BPF_JSET >> 4] = "jset", [EBPF_JNE >> 4] = "jne", > + [EBPF_JSGT >> 4] = "jsgt", [EBPF_JSGE >> 4] = "jsge", > + [EBPF_CALL >> 4] = "call", [EBPF_EXIT >> 4] = "exit", > +}; > + > +static void ebpf_dump(FILE *f, const struct ebpf_insn insn, size_t n) > +{ > + const char *op, *postfix = ""; > + uint8_t cls = BPF_CLASS(insn.code); > + > + fprintf(f, " L%zu:\t", n); > + > + switch (cls) { > + default: > + fprintf(f, "unimp 0x%x // class: %s\n", insn.code, > + class_tbl[cls]); > + break; > + case BPF_ALU: > + postfix = "32"; > + /* fall through */ > + case EBPF_ALU64: > + op = alu_op_tbl[BPF_OP_INDEX(insn.code)]; > + if (BPF_SRC(insn.code) == BPF_X) > + fprintf(f, "%s%s r%u, r%u\n", op, postfix, insn.dst_reg, > + insn.src_reg); > + else > + fprintf(f, "%s%s r%u, #0x%x\n", op, postfix, > + insn.dst_reg, insn.imm); > + break; > + case BPF_LD: > + op = "ld"; > + postfix = size_tbl[BPF_SIZE_INDEX(insn.code)]; > + if (BPF_MODE(insn.code) == BPF_IMM) > + fprintf(f, "%s%s r%d, #0x%x\n", op, postfix, > + insn.dst_reg, insn.imm); > + else if (BPF_MODE(insn.code) == BPF_ABS) > + fprintf(f, "%s%s r%d, [%d]\n", op, postfix, > + insn.dst_reg, insn.imm); > + else if (BPF_MODE(insn.code) == BPF_IND) > + fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix, > + insn.dst_reg, insn.src_reg, insn.imm); > + else > + fprintf(f, "// BUG: LD opcode 0x%02x in eBPF insns\n", > + insn.code); > + break; > + case BPF_LDX: > + op = "ldx"; > + postfix = size_tbl[BPF_SIZE_INDEX(insn.code)]; > + fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix, insn.dst_reg, > + insn.src_reg, insn.off); > + break; > +#define L(pc, off) ((int)(pc) + 1 + (off)) > + case BPF_JMP: > + op = jump_tbl[BPF_OP_INDEX(insn.code)]; > + if (op == NULL) > + fprintf(f, "invalid jump opcode: %#x\n", insn.code); > + else if (BPF_OP(insn.code) == BPF_JA) > + fprintf(f, "%s L%d\n", op, L(n, insn.off)); > + else if (BPF_OP(insn.code) == EBPF_EXIT) > + fprintf(f, "%s\n", op); > + else > + fprintf(f, "%s r%u, #0x%x, L%d\n", op, insn.dst_reg, > + insn.imm, L(n, insn.off)); > + break; > + case BPF_RET: > + fprintf(f, "// BUG: RET opcode 0x%02x in eBPF insns\n", > + insn.code); > + break; > + } > +} > + > +void rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len) > +{ > + uint32_t i; > + > + for (i = 0; i < len; ++i) > + ebpf_dump(f, buf[i], i); > +} > diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build > index 54f7610ae990..5b5585173aeb 100644 > --- a/lib/bpf/meson.build > +++ b/lib/bpf/meson.build > @@ -2,6 +2,7 @@ > # Copyright(c) 2018 Intel Corporation > > sources = files('bpf.c', > + 'bpf_dump.c', > 'bpf_exec.c', > 'bpf_load.c', > 'bpf_pkt.c', > diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h > index 2f23e272a376..0d0a84b130a0 100644 > --- a/lib/bpf/rte_bpf.h > +++ b/lib/bpf/rte_bpf.h > @@ -198,6 +198,20 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], > int > rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit); > > +/** > + * Dump epf instructions to a file. > + * > + * @param f > + * A pointer to a file for output > + * @param buf > + * A pointer to BPF instructions > + * @param len > + * Number of BPF instructions to dump. > + */ > +__rte_experimental > +void > +rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len); > + > #ifdef RTE_PORT_PCAP > > struct bpf_program; > diff --git a/lib/bpf/version.map b/lib/bpf/version.map > index 47082d5003ef..3b953f2f4592 100644 > --- a/lib/bpf/version.map > +++ b/lib/bpf/version.map > @@ -19,4 +19,5 @@ EXPERIMENTAL { > global: > > rte_bpf_convert; > + rte_bpf_dump; > }; > -- Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com> > 2.30.2
On Wed, 15 Sep 2021 11:04:41 +0000 "Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote: > > diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c > > index a46ffeb067dd..db84add7dcce 100644 > > --- a/lib/bpf/bpf_convert.c > > +++ b/lib/bpf/bpf_convert.c > > @@ -331,7 +331,12 @@ static int bpf_convert_filter(const struct bpf_insn *prog, size_t len, > > case BPF_LD | BPF_IND | BPF_H: > > case BPF_LD | BPF_IND | BPF_B: > > /* All arithmetic insns map as-is. */ > > - *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k); > > + insn->code = fp->code; > > + insn->dst_reg = BPF_REG_A; > > + bpf_src = BPF_SRC(fp->code); > > + insn->src_reg = bpf_src == BPF_X ? BPF_REG_X : 0; > > + insn->off = 0; > > + insn->imm = fp->k; > > break; > > Should it be part of that patch? > Looks like belongs to previous one, no? Yes, moved it in next bundle.
diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c index a46ffeb067dd..db84add7dcce 100644 --- a/lib/bpf/bpf_convert.c +++ b/lib/bpf/bpf_convert.c @@ -331,7 +331,12 @@ static int bpf_convert_filter(const struct bpf_insn *prog, size_t len, case BPF_LD | BPF_IND | BPF_H: case BPF_LD | BPF_IND | BPF_B: /* All arithmetic insns map as-is. */ - *insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k); + insn->code = fp->code; + insn->dst_reg = BPF_REG_A; + bpf_src = BPF_SRC(fp->code); + insn->src_reg = bpf_src == BPF_X ? BPF_REG_X : 0; + insn->off = 0; + insn->imm = fp->k; break; /* Jump transformation cannot use BPF block macros diff --git a/lib/bpf/bpf_dump.c b/lib/bpf/bpf_dump.c new file mode 100644 index 000000000000..a6a431e64903 --- /dev/null +++ b/lib/bpf/bpf_dump.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2021 Stephen Hemminger + * Based on filter2xdp + * Copyright (C) 2017 Tobias Klauser + */ + +#include <stdio.h> +#include <stdint.h> + +#include "rte_bpf.h" + +#define BPF_OP_INDEX(x) (BPF_OP(x) >> 4) +#define BPF_SIZE_INDEX(x) (BPF_SIZE(x) >> 3) + +static const char *const class_tbl[] = { + [BPF_LD] = "ld", [BPF_LDX] = "ldx", [BPF_ST] = "st", + [BPF_STX] = "stx", [BPF_ALU] = "alu", [BPF_JMP] = "jmp", + [BPF_RET] = "ret", [BPF_MISC] = "alu64", +}; + +static const char *const alu_op_tbl[16] = { + [BPF_ADD >> 4] = "add", [BPF_SUB >> 4] = "sub", + [BPF_MUL >> 4] = "mul", [BPF_DIV >> 4] = "div", + [BPF_OR >> 4] = "or", [BPF_AND >> 4] = "and", + [BPF_LSH >> 4] = "lsh", [BPF_RSH >> 4] = "rsh", + [BPF_NEG >> 4] = "neg", [BPF_MOD >> 4] = "mod", + [BPF_XOR >> 4] = "xor", [EBPF_MOV >> 4] = "mov", + [EBPF_ARSH >> 4] = "arsh", [EBPF_END >> 4] = "endian", +}; + +static const char *const size_tbl[] = { + [BPF_W >> 3] = "w", + [BPF_H >> 3] = "h", + [BPF_B >> 3] = "b", + [EBPF_DW >> 3] = "dw", +}; + +static const char *const jump_tbl[16] = { + [BPF_JA >> 4] = "ja", [BPF_JEQ >> 4] = "jeq", + [BPF_JGT >> 4] = "jgt", [BPF_JGE >> 4] = "jge", + [BPF_JSET >> 4] = "jset", [EBPF_JNE >> 4] = "jne", + [EBPF_JSGT >> 4] = "jsgt", [EBPF_JSGE >> 4] = "jsge", + [EBPF_CALL >> 4] = "call", [EBPF_EXIT >> 4] = "exit", +}; + +static void ebpf_dump(FILE *f, const struct ebpf_insn insn, size_t n) +{ + const char *op, *postfix = ""; + uint8_t cls = BPF_CLASS(insn.code); + + fprintf(f, " L%zu:\t", n); + + switch (cls) { + default: + fprintf(f, "unimp 0x%x // class: %s\n", insn.code, + class_tbl[cls]); + break; + case BPF_ALU: + postfix = "32"; + /* fall through */ + case EBPF_ALU64: + op = alu_op_tbl[BPF_OP_INDEX(insn.code)]; + if (BPF_SRC(insn.code) == BPF_X) + fprintf(f, "%s%s r%u, r%u\n", op, postfix, insn.dst_reg, + insn.src_reg); + else + fprintf(f, "%s%s r%u, #0x%x\n", op, postfix, + insn.dst_reg, insn.imm); + break; + case BPF_LD: + op = "ld"; + postfix = size_tbl[BPF_SIZE_INDEX(insn.code)]; + if (BPF_MODE(insn.code) == BPF_IMM) + fprintf(f, "%s%s r%d, #0x%x\n", op, postfix, + insn.dst_reg, insn.imm); + else if (BPF_MODE(insn.code) == BPF_ABS) + fprintf(f, "%s%s r%d, [%d]\n", op, postfix, + insn.dst_reg, insn.imm); + else if (BPF_MODE(insn.code) == BPF_IND) + fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix, + insn.dst_reg, insn.src_reg, insn.imm); + else + fprintf(f, "// BUG: LD opcode 0x%02x in eBPF insns\n", + insn.code); + break; + case BPF_LDX: + op = "ldx"; + postfix = size_tbl[BPF_SIZE_INDEX(insn.code)]; + fprintf(f, "%s%s r%d, [r%u + %d]\n", op, postfix, insn.dst_reg, + insn.src_reg, insn.off); + break; +#define L(pc, off) ((int)(pc) + 1 + (off)) + case BPF_JMP: + op = jump_tbl[BPF_OP_INDEX(insn.code)]; + if (op == NULL) + fprintf(f, "invalid jump opcode: %#x\n", insn.code); + else if (BPF_OP(insn.code) == BPF_JA) + fprintf(f, "%s L%d\n", op, L(n, insn.off)); + else if (BPF_OP(insn.code) == EBPF_EXIT) + fprintf(f, "%s\n", op); + else + fprintf(f, "%s r%u, #0x%x, L%d\n", op, insn.dst_reg, + insn.imm, L(n, insn.off)); + break; + case BPF_RET: + fprintf(f, "// BUG: RET opcode 0x%02x in eBPF insns\n", + insn.code); + break; + } +} + +void rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len) +{ + uint32_t i; + + for (i = 0; i < len; ++i) + ebpf_dump(f, buf[i], i); +} diff --git a/lib/bpf/meson.build b/lib/bpf/meson.build index 54f7610ae990..5b5585173aeb 100644 --- a/lib/bpf/meson.build +++ b/lib/bpf/meson.build @@ -2,6 +2,7 @@ # Copyright(c) 2018 Intel Corporation sources = files('bpf.c', + 'bpf_dump.c', 'bpf_exec.c', 'bpf_load.c', 'bpf_pkt.c', diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h index 2f23e272a376..0d0a84b130a0 100644 --- a/lib/bpf/rte_bpf.h +++ b/lib/bpf/rte_bpf.h @@ -198,6 +198,20 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], int rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit); +/** + * Dump epf instructions to a file. + * + * @param f + * A pointer to a file for output + * @param buf + * A pointer to BPF instructions + * @param len + * Number of BPF instructions to dump. + */ +__rte_experimental +void +rte_bpf_dump(FILE *f, const struct ebpf_insn *buf, uint32_t len); + #ifdef RTE_PORT_PCAP struct bpf_program; diff --git a/lib/bpf/version.map b/lib/bpf/version.map index 47082d5003ef..3b953f2f4592 100644 --- a/lib/bpf/version.map +++ b/lib/bpf/version.map @@ -19,4 +19,5 @@ EXPERIMENTAL { global: rte_bpf_convert; + rte_bpf_dump; };
When debugging converted (and other) programs it is useful to see disassembled eBPF output. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- lib/bpf/bpf_convert.c | 7 ++- lib/bpf/bpf_dump.c | 118 ++++++++++++++++++++++++++++++++++++++++++ lib/bpf/meson.build | 1 + lib/bpf/rte_bpf.h | 14 +++++ lib/bpf/version.map | 1 + 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 lib/bpf/bpf_dump.c