/* * Copyright (c) 2015-2017 Dmitry V. Levin * Copyright (c) 2017 Quentin Monnet * Copyright (c) 2015-2018 The strace developers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "defs.h" #include "print_fields.h" #ifdef HAVE_LINUX_BPF_H # include #endif #include #include "bpf_attr.h" #include "xlat/bpf_commands.h" #include "xlat/bpf_file_mode_flags.h" #include "xlat/bpf_map_types.h" #include "xlat/bpf_map_flags.h" #include "xlat/bpf_prog_types.h" #include "xlat/bpf_prog_flags.h" #include "xlat/bpf_map_update_elem_flags.h" #include "xlat/bpf_attach_type.h" #include "xlat/bpf_attach_flags.h" #include "xlat/bpf_query_flags.h" #include "xlat/ebpf_regs.h" #include "xlat/numa_node.h" #define DECL_BPF_CMD_DECODER(bpf_cmd_decoder) \ int \ bpf_cmd_decoder(struct tcb *const tcp, \ const kernel_ulong_t addr, \ const unsigned int size, \ void *const data) \ /* End of DECL_BPF_CMD_DECODER definition. */ #define BEGIN_BPF_CMD_DECODER(bpf_cmd) \ static DECL_BPF_CMD_DECODER(decode_ ## bpf_cmd) \ { \ struct bpf_cmd ## _struct attr = {}; \ const size_t attr_size = bpf_cmd ## _struct_size; \ const unsigned int len = MIN(size, attr_size); \ memcpy(&attr, data, len); \ do { \ /* End of BEGIN_BPF_CMD_DECODER definition. */ #define END_BPF_CMD_DECODER(rval) \ decode_attr_extra_data(tcp, data, size, attr_size); \ } while (0); \ tprints("}"); \ return (rval); \ } \ /* End of END_BPF_CMD_DECODER definition. */ #define BPF_CMD_ENTRY(bpf_cmd) \ [bpf_cmd] = decode_ ## bpf_cmd typedef DECL_BPF_CMD_DECODER((*bpf_cmd_decoder_t)); /* * A note about bpf syscall decoder: it doesn't perform any size sanity checks, * so even if it leads to partial copying of one of the fields, the command * handler will still use the (partially-copied-from-userspace, partially * zeroed) field value. That's why we stop decoding and check for known sizes * that correspond to released versions of the structure used by the specific * command - it looks like the most sensible way to parse this insanity. */ static int decode_attr_extra_data(struct tcb *const tcp, const char *data, unsigned int size, const size_t attr_size) { if (size <= attr_size) return 0; data += attr_size; size -= attr_size; unsigned int i; for (i = 0; i < size; ++i) { if (data[i]) { tprints(", "); if (abbrev(tcp)) { tprints("..."); } else { tprintf("/* bytes %zu..%zu */ ", attr_size, attr_size + size - 1); print_quoted_string(data, size, QUOTE_FORCE_HEX); } return RVAL_DECODED; } } return 0; } struct ebpf_insn { uint8_t code; uint8_t dst_reg:4; uint8_t src_reg:4; int16_t off; int32_t imm; }; struct ebpf_insns_data { unsigned int count; }; static bool print_ebpf_insn(struct tcb * const tcp, void * const elem_buf, const size_t elem_size, void * const data) { struct ebpf_insns_data *eid = data; struct ebpf_insn *insn = elem_buf; if (eid->count++ >= BPF_MAXINSNS) { tprints("..."); return false; } tprints("{code="); print_bpf_filter_code(insn->code, true); /* We can't use PRINT_FIELD_XVAL on bit fields */ tprints(", dst_reg="); printxval_index(ebpf_regs, insn->dst_reg, "BPF_REG_???"); tprints(", src_reg="); printxval_index(ebpf_regs, insn->src_reg, "BPF_REG_???"); PRINT_FIELD_D(", ", *insn, off); PRINT_FIELD_X(", ", *insn, imm); tprints("}"); return true; } static void print_ebpf_prog(struct tcb *const tcp, const uint64_t addr, const uint32_t len) { print_big_u64_addr(addr); if (abbrev(tcp)) { printaddr(addr); } else { struct ebpf_insns_data eid = {}; struct ebpf_insn insn; print_array(tcp, addr, len, &insn, sizeof(insn), tfetch_mem, print_ebpf_insn, &eid); } } BEGIN_BPF_CMD_DECODER(BPF_MAP_CREATE) { PRINT_FIELD_XVAL_INDEX("{", attr, map_type, bpf_map_types, "BPF_MAP_TYPE_???"); PRINT_FIELD_U(", ", attr, key_size); PRINT_FIELD_U(", ", attr, value_size); PRINT_FIELD_U(", ", attr, max_entries); /* map_flags field was added in Linux commit v4.6-rc1~91^2~108^2~6. */ if (len <= offsetof(struct BPF_MAP_CREATE_struct, map_flags)) break; PRINT_FIELD_FLAGS(", ", attr, map_flags, bpf_map_flags, "BPF_F_???"); /* * inner_map_fd field was added in Linux commit * v4.12-rc1~64^3~373^2~2. */ if (len <= offsetof(struct BPF_MAP_CREATE_struct, inner_map_fd)) break; PRINT_FIELD_FD(", ", attr, inner_map_fd, tcp); /* numa_node field was added in Linux commit v4.14-rc1~130^2~196^2~1. */ if (len <= offsetof(struct BPF_MAP_CREATE_struct, numa_node)) break; if (attr.map_flags & BPF_F_NUMA_NODE) { /* * Kernel uses the value of -1 as a designation for "no NUMA * node specified", and even uses NUMA_NO_NODE constant; * however, the constant definition is not a part of UAPI * headers, thus we can't simply print this named constant * instead of the value. Let's force verbose xlat style instead * in order to provide the information for the user while * not hampering the availability to derive the actual value * without the access to the kernel headers. */ tprints(", numa_node="); printxvals_ex(attr.numa_node, NULL, XLAT_STYLE_FMT_U | XLAT_STYLE_VERBOSE, numa_node, NULL); } /* map_name field was added in Linux commit v4.15-rc1~84^2~605^2~3. */ if (len <= offsetof(struct BPF_MAP_CREATE_struct, map_name)) break; PRINT_FIELD_CSTRING_SZ(", ", attr, map_name, MIN(sizeof(attr.map_name), len - offsetof(struct BPF_MAP_CREATE_struct, map_name))); /* * map_ifindex field was added in Linux commit * v4.16-rc1~123^2~145^2~5^2~8. */ if (len <= offsetof(struct BPF_MAP_CREATE_struct, map_ifindex)) break; PRINT_FIELD_IFINDEX(", ", attr, map_ifindex); } END_BPF_CMD_DECODER(RVAL_DECODED | RVAL_FD) BEGIN_BPF_CMD_DECODER(BPF_MAP_LOOKUP_ELEM) { PRINT_FIELD_FD("{", attr, map_fd, tcp); PRINT_FIELD_ADDR64(", ", attr, key); PRINT_FIELD_ADDR64(", ", attr, value); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_MAP_UPDATE_ELEM) { PRINT_FIELD_FD("{", attr, map_fd, tcp); PRINT_FIELD_ADDR64(", ", attr, key); PRINT_FIELD_ADDR64(", ", attr, value); PRINT_FIELD_XVAL_INDEX(", ", attr, flags, bpf_map_update_elem_flags, "BPF_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_MAP_DELETE_ELEM) { PRINT_FIELD_FD("{", attr, map_fd, tcp); PRINT_FIELD_ADDR64(", ", attr, key); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_MAP_GET_NEXT_KEY) { PRINT_FIELD_FD("{", attr, map_fd, tcp); PRINT_FIELD_ADDR64(", ", attr, key); PRINT_FIELD_ADDR64(", ", attr, next_key); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_PROG_LOAD) { PRINT_FIELD_XVAL_INDEX("{", attr, prog_type, bpf_prog_types, "BPF_PROG_TYPE_???"); PRINT_FIELD_U(", ", attr, insn_cnt); tprints(", insns="); print_ebpf_prog(tcp, attr.insns, attr.insn_cnt); tprintf(", license="); print_big_u64_addr(attr.license); printstr(tcp, attr.license); /* log_* fields were added in Linux commit v3.18-rc1~52^2~1^2~4. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, log_level)) break; PRINT_FIELD_U(", ", attr, log_level); PRINT_FIELD_U(", ", attr, log_size); tprintf(", log_buf="); print_big_u64_addr(attr.log_buf); printstr_ex(tcp, attr.log_buf, attr.log_size, QUOTE_0_TERMINATED); /* kern_version field was added in Linux commit v4.1-rc1~84^2~50. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, kern_version)) break; tprintf(", kern_version=KERNEL_VERSION(%u, %u, %u)", attr.kern_version >> 16, (attr.kern_version >> 8) & 0xFF, attr.kern_version & 0xFF); /* prog_flags field was added in Linux commit v4.12-rc2~34^2~29^2~2. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, prog_flags)) break; PRINT_FIELD_FLAGS(", ", attr, prog_flags, bpf_prog_flags, "BPF_F_???"); /* prog_name field was added in Linux commit v4.15-rc1~84^2~605^2~4. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, prog_name)) break; PRINT_FIELD_CSTRING_SZ(", ", attr, prog_name, MIN(sizeof(attr.prog_name), len - offsetof(struct BPF_PROG_LOAD_struct, prog_name))); /* * prog_ifindex field was added as prog_target_ifindex in Linux commit * v4.15-rc1~84^2~127^2~13 and renamed to its current name in * v4.15-rc1~15^2~5^2~3^2~7. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, prog_ifindex)) break; PRINT_FIELD_IFINDEX(", ", attr, prog_ifindex); /* * expected_attach_type was added in Linux commit * v4.17-rc1~148^2~19^2^2~8. */ if (len <= offsetof(struct BPF_PROG_LOAD_struct, expected_attach_type)) break; PRINT_FIELD_XVAL(", ", attr, expected_attach_type, bpf_attach_type, "BPF_???"); } END_BPF_CMD_DECODER(RVAL_DECODED | RVAL_FD) BEGIN_BPF_CMD_DECODER(BPF_OBJ_PIN) { tprintf("{pathname="); print_big_u64_addr(attr.pathname); printpath(tcp, attr.pathname); PRINT_FIELD_FD(", ", attr, bpf_fd, tcp); /* file_flags field was added in Linux v4.15-rc1~84^2~384^2~4 */ if (len <= offsetof(struct BPF_OBJ_PIN_struct, file_flags)) break; PRINT_FIELD_FLAGS(", ", attr, file_flags, bpf_file_mode_flags, "BPF_F_???"); } END_BPF_CMD_DECODER(RVAL_DECODED | RVAL_FD) #define decode_BPF_OBJ_GET decode_BPF_OBJ_PIN BEGIN_BPF_CMD_DECODER(BPF_PROG_ATTACH) { PRINT_FIELD_FD("{", attr, target_fd, tcp); PRINT_FIELD_FD(", ", attr, attach_bpf_fd, tcp); PRINT_FIELD_XVAL_INDEX(", ", attr, attach_type, bpf_attach_type, "BPF_???"); PRINT_FIELD_FLAGS(", ", attr, attach_flags, bpf_attach_flags, "BPF_F_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_PROG_DETACH) { PRINT_FIELD_FD("{", attr, target_fd, tcp); PRINT_FIELD_XVAL_INDEX(", ", attr, attach_type, bpf_attach_type, "BPF_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_PROG_TEST_RUN) { PRINT_FIELD_FD("{test={", attr, prog_fd, tcp); PRINT_FIELD_U(", ", attr, retval); PRINT_FIELD_U(", ", attr, data_size_in); PRINT_FIELD_U(", ", attr, data_size_out); PRINT_FIELD_ADDR64(", ", attr, data_in); PRINT_FIELD_ADDR64(", ", attr, data_out); PRINT_FIELD_U(", ", attr, repeat); PRINT_FIELD_U(", ", attr, duration); tprints("}"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_PROG_GET_NEXT_ID) { PRINT_FIELD_U("{", attr, start_id); PRINT_FIELD_U(", ", attr, next_id); /* open_flags field has been added in Linux v4.15-rc1~84^2~384^2~4 */ if (len <= offsetof(struct BPF_PROG_GET_NEXT_ID_struct, open_flags)) break; PRINT_FIELD_FLAGS(", ", attr, open_flags, bpf_file_mode_flags, "BPF_F_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) #define decode_BPF_MAP_GET_NEXT_ID decode_BPF_PROG_GET_NEXT_ID BEGIN_BPF_CMD_DECODER(BPF_PROG_GET_FD_BY_ID) { PRINT_FIELD_U("{", attr, prog_id); PRINT_FIELD_U(", ", attr, next_id); /* open_flags field has been added in Linux v4.15-rc1~84^2~384^2~4 */ if (len <= offsetof(struct BPF_PROG_GET_FD_BY_ID_struct, open_flags)) break; PRINT_FIELD_FLAGS(", ", attr, open_flags, bpf_file_mode_flags, "BPF_F_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_MAP_GET_FD_BY_ID) { PRINT_FIELD_U("{", attr, map_id); PRINT_FIELD_U(", ", attr, next_id); /* open_flags field has been added in Linux v4.15-rc1~84^2~384^2~4 */ if (len <= offsetof(struct BPF_MAP_GET_FD_BY_ID_struct, open_flags)) break; PRINT_FIELD_FLAGS(", ", attr, open_flags, bpf_file_mode_flags, "BPF_F_???"); } END_BPF_CMD_DECODER(RVAL_DECODED) struct obj_get_info_saved; typedef void (*print_bpf_obj_info_fn)(struct tcb *, uint32_t bpf_fd, const char *info_buf, uint32_t size, struct obj_get_info_saved *saved); struct obj_get_info_saved { print_bpf_obj_info_fn print_fn; uint32_t info_len; uint32_t jited_prog_len; uint32_t xlated_prog_len; uint32_t nr_map_ids; }; static void print_bpf_map_info(struct tcb * const tcp, uint32_t bpf_fd, const char *info_buf, uint32_t size, struct obj_get_info_saved *saved) { if (entering(tcp)) return; struct bpf_map_info_struct info = { 0 }; const unsigned int len = MIN(size, bpf_map_info_struct_size); memcpy(&info, info_buf, len); PRINT_FIELD_XVAL("{", info, type, bpf_map_types, "BPF_MAP_TYPE_???"); PRINT_FIELD_U(", ", info, id); PRINT_FIELD_U(", ", info, key_size); PRINT_FIELD_U(", ", info, value_size); PRINT_FIELD_U(", ", info, max_entries); PRINT_FIELD_FLAGS(", ", info, map_flags, bpf_map_flags, "BPF_F_???"); /* * "name" field was introduced by Linux commit v4.15-rc1~84^2~605^2~3. */ if (len <= offsetof(struct bpf_map_info_struct, name)) goto print_bpf_map_info_end; PRINT_FIELD_CSTRING(", ", info, name); /* * ifindex, netns_dev, and netns_ino fields were introduced * by Linux commit v4.16-rc1~123^2~109^2~5^2~4. */ if (len <= offsetof(struct bpf_map_info_struct, ifindex)) goto print_bpf_map_info_end; PRINT_FIELD_IFINDEX(", ", info, ifindex); PRINT_FIELD_DEV(", ", info, netns_dev); PRINT_FIELD_U(", ", info, netns_ino); decode_attr_extra_data(tcp, info_buf, size, bpf_map_info_struct_size); print_bpf_map_info_end: tprints("}"); } static void print_bpf_prog_info(struct tcb * const tcp, uint32_t bpf_fd, const char *info_buf, uint32_t size, struct obj_get_info_saved *saved) { struct bpf_prog_info_struct info = { 0 }; const unsigned int len = MIN(size, bpf_prog_info_struct_size); uint64_t map_id_buf; memcpy(&info, info_buf, len); if (entering(tcp)) { saved->jited_prog_len = info.jited_prog_len; saved->xlated_prog_len = info.xlated_prog_len; saved->nr_map_ids = info.nr_map_ids; return; } PRINT_FIELD_XVAL("{", info, type, bpf_prog_types, "BPF_PROG_TYPE_???"); PRINT_FIELD_U(", ", info, id); PRINT_FIELD_HEX_ARRAY(", ", info, tag); tprints(", jited_prog_len="); if (saved->jited_prog_len != info.jited_prog_len) tprintf("%" PRIu32 " => ", saved->jited_prog_len); tprintf("%" PRIu32, info.jited_prog_len); tprints(", jited_prog_insns="); print_big_u64_addr(info.jited_prog_insns); printstr_ex(tcp, info.jited_prog_insns, info.jited_prog_len, QUOTE_FORCE_HEX); tprints(", xlated_prog_len="); if (saved->xlated_prog_len != info.xlated_prog_len) tprintf("%" PRIu32 " => ", saved->xlated_prog_len); tprintf("%" PRIu32, info.xlated_prog_len); tprints(", xlated_prog_insns="); print_ebpf_prog(tcp, info.xlated_prog_insns, MIN(saved->xlated_prog_len, info.xlated_prog_len) / 8); /* * load_time, created_by_uid, nr_map_ids, map_ids, and name fields * were introduced by Linux commit v4.15-rc1~84^2~605^2~4. */ if (len <= offsetof(struct bpf_prog_info_struct, load_time)) goto print_bpf_prog_info_end; PRINT_FIELD_U(", ", info, load_time); PRINT_FIELD_UID(", ", info, created_by_uid); tprints(", nr_map_ids="); if (saved->nr_map_ids != info.nr_map_ids) tprintf("%" PRIu32 " => ", saved->nr_map_ids); tprintf("%" PRIu32, info.nr_map_ids); tprints(", map_ids="); print_big_u64_addr(info.map_ids); print_array(tcp, info.map_ids, MIN(saved->nr_map_ids, info.nr_map_ids), &map_id_buf, sizeof(map_id_buf), tfetch_mem, print_uint32_array_member, 0); PRINT_FIELD_CSTRING(", ", info, name); /* * ifindex, netns_dev, and netns_ino fields were introduced * by Linux commit v4.16-rc1~123^2~227^2~5^2~2. */ if (len <= offsetof(struct bpf_prog_info_struct, ifindex)) goto print_bpf_prog_info_end; PRINT_FIELD_IFINDEX(", ", info, ifindex); PRINT_FIELD_DEV(", ", info, netns_dev); PRINT_FIELD_U(", ", info, netns_ino); decode_attr_extra_data(tcp, info_buf, size, bpf_prog_info_struct_size); print_bpf_prog_info_end: tprints("}"); } static const char * fetch_bpf_obj_info(struct tcb * const tcp, uint64_t info, uint32_t size) { static char *info_buf; if (!info_buf) info_buf = xmalloc(get_pagesize()); memset(info_buf, 0, get_pagesize()); if (size > 0 && size <= get_pagesize() && !umoven(tcp, info, size, info_buf)) return info_buf; return NULL; } static void print_bpf_obj_info_addr(struct tcb * const tcp, uint64_t addr) { if (exiting(tcp)) printaddr64(addr); } static void print_bpf_obj_info(struct tcb * const tcp, uint32_t bpf_fd, uint64_t info, uint32_t size, struct obj_get_info_saved *saved) { if (abbrev(tcp)) { print_bpf_obj_info_addr(tcp, info); return; } static struct { const char *id; print_bpf_obj_info_fn print_fn; } obj_printers[] = { { "anon_inode:bpf-map", print_bpf_map_info }, { "anon_inode:bpf-prog", print_bpf_prog_info } }; if (entering(tcp)) { char path[PATH_MAX + 1]; if (getfdpath(tcp, bpf_fd, path, sizeof(path)) > 0) { for (size_t i = 0; i < ARRAY_SIZE(obj_printers); ++i) { if (!strcmp(path, obj_printers[i].id)) { saved->print_fn = obj_printers[i].print_fn; break; } } } } if (!saved || !saved->print_fn) { print_bpf_obj_info_addr(tcp, info); return; } const char *info_buf = fetch_bpf_obj_info(tcp, info, size); if (info_buf) saved->print_fn(tcp, bpf_fd, info_buf, size, saved); else print_bpf_obj_info_addr(tcp, info); } BEGIN_BPF_CMD_DECODER(BPF_OBJ_GET_INFO_BY_FD) { struct obj_get_info_saved *saved; if (entering(tcp)) { saved = xcalloc(1, sizeof(*saved)); saved->info_len = attr.info_len; set_tcb_priv_data(tcp, saved, free); PRINT_FIELD_FD("{info={", attr, bpf_fd, tcp); PRINT_FIELD_U(", ", attr, info_len); } else { saved = get_tcb_priv_data(tcp); if (saved && (saved->info_len != attr.info_len)) tprintf(" => %u", attr.info_len); tprintf(", info="); } print_bpf_obj_info(tcp, attr.bpf_fd, attr.info, attr.info_len, saved); if (entering(tcp)) return 0; tprints("}"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_PROG_QUERY) { uint32_t prog_id_buf; if (entering(tcp)) { PRINT_FIELD_FD("{query={", attr, target_fd, tcp); PRINT_FIELD_XVAL_INDEX(", ", attr, attach_type, bpf_attach_type, "BPF_???"); PRINT_FIELD_FLAGS(", ", attr, query_flags, bpf_query_flags, "BPF_F_QUERY_???"); PRINT_FIELD_FLAGS(", ", attr, attach_flags, bpf_attach_flags, "BPF_F_???"); tprints(", prog_ids="); set_tcb_priv_ulong(tcp, attr.prog_cnt); return 0; } print_big_u64_addr(attr.prog_ids); print_array(tcp, attr.prog_ids, attr.prog_cnt, &prog_id_buf, sizeof(prog_id_buf), tfetch_mem, print_uint32_array_member, 0); tprints(", prog_cnt="); const uint32_t prog_cnt_entering = get_tcb_priv_ulong(tcp); if (prog_cnt_entering != attr.prog_cnt) tprintf("%" PRIu32 " => ", prog_cnt_entering); tprintf("%" PRIu32, attr.prog_cnt); tprints("}"); } END_BPF_CMD_DECODER(RVAL_DECODED) BEGIN_BPF_CMD_DECODER(BPF_RAW_TRACEPOINT_OPEN) { enum { TP_NAME_SIZE = 128 }; tprintf("{raw_tracepoint={name="); print_big_u64_addr(attr.name); printstr_ex(tcp, attr.name, TP_NAME_SIZE, QUOTE_0_TERMINATED); PRINT_FIELD_FD(", ", attr, prog_fd, tcp); tprints("}"); } END_BPF_CMD_DECODER(RVAL_DECODED) SYS_FUNC(bpf) { static const bpf_cmd_decoder_t bpf_cmd_decoders[] = { BPF_CMD_ENTRY(BPF_MAP_CREATE), BPF_CMD_ENTRY(BPF_MAP_LOOKUP_ELEM), BPF_CMD_ENTRY(BPF_MAP_UPDATE_ELEM), BPF_CMD_ENTRY(BPF_MAP_DELETE_ELEM), BPF_CMD_ENTRY(BPF_MAP_GET_NEXT_KEY), BPF_CMD_ENTRY(BPF_PROG_LOAD), BPF_CMD_ENTRY(BPF_OBJ_PIN), BPF_CMD_ENTRY(BPF_OBJ_GET), BPF_CMD_ENTRY(BPF_PROG_ATTACH), BPF_CMD_ENTRY(BPF_PROG_DETACH), BPF_CMD_ENTRY(BPF_PROG_TEST_RUN), BPF_CMD_ENTRY(BPF_PROG_GET_NEXT_ID), BPF_CMD_ENTRY(BPF_MAP_GET_NEXT_ID), BPF_CMD_ENTRY(BPF_PROG_GET_FD_BY_ID), BPF_CMD_ENTRY(BPF_MAP_GET_FD_BY_ID), BPF_CMD_ENTRY(BPF_OBJ_GET_INFO_BY_FD), BPF_CMD_ENTRY(BPF_PROG_QUERY), BPF_CMD_ENTRY(BPF_RAW_TRACEPOINT_OPEN), }; const unsigned int cmd = tcp->u_arg[0]; const kernel_ulong_t addr = tcp->u_arg[1]; const unsigned int size = tcp->u_arg[2]; int rc = RVAL_DECODED; if (entering(tcp)) { printxval_index(bpf_commands, cmd, "BPF_???"); tprints(", "); } if (size > 0 && size <= get_pagesize() && cmd < ARRAY_SIZE(bpf_cmd_decoders) && bpf_cmd_decoders[cmd]) { static char *buf; if (!buf) buf = xmalloc(get_pagesize()); if (!umoven_or_printaddr_ignore_syserror(tcp, addr, size, buf)) rc = bpf_cmd_decoders[cmd](tcp, addr, size, buf); } else { printaddr(addr); } if (exiting(tcp) || (rc & RVAL_DECODED)) tprintf(", %u", size); return rc; }