From 71bb428fe2c19512ac671d5ee16ef3e73e1b49a8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 4 Oct 2017 20:10:04 -0700 Subject: tools: bpf: add bpftool Add a simple tool for querying and updating BPF objects on the system. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 456 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 tools/bpf/bpftool/prog.c (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c new file mode 100644 index 000000000000..421ba89ce86a --- /dev/null +++ b/tools/bpf/bpftool/prog.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* Author: Jakub Kicinski */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "main.h" + +static const char * const prog_type_name[] = { + [BPF_PROG_TYPE_UNSPEC] = "unspec", + [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", + [BPF_PROG_TYPE_KPROBE] = "kprobe", + [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", + [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", + [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", + [BPF_PROG_TYPE_XDP] = "xdp", + [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", + [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", + [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", + [BPF_PROG_TYPE_LWT_IN] = "lwt_in", + [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", + [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", + [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", + [BPF_PROG_TYPE_SK_SKB] = "sk_skb", +}; + +static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) +{ + struct timespec real_time_ts, boot_time_ts; + time_t wallclock_secs; + struct tm load_tm; + + buf[--size] = '\0'; + + if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || + clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { + perror("Can't read clocks"); + snprintf(buf, size, "%llu", nsecs / 1000000000); + return; + } + + wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + + nsecs / 1000000000; + + if (!localtime_r(&wallclock_secs, &load_tm)) { + snprintf(buf, size, "%llu", nsecs / 1000000000); + return; + } + + strftime(buf, size, "%b %d/%H:%M", &load_tm); +} + +static int prog_fd_by_tag(unsigned char *tag) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + unsigned int id = 0; + int err; + int fd; + + while (true) { + err = bpf_prog_get_next_id(id, &id); + if (err) { + err("%s\n", strerror(errno)); + return -1; + } + + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0) { + err("can't get prog by id (%u): %s\n", + id, strerror(errno)); + return -1; + } + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) { + err("can't get prog info (%u): %s\n", + id, strerror(errno)); + close(fd); + return -1; + } + + if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) + return fd; + + close(fd); + } +} + +int prog_parse_fd(int *argc, char ***argv) +{ + int fd; + + if (is_prefix(**argv, "id")) { + unsigned int id; + char *endptr; + + NEXT_ARGP(); + + id = strtoul(**argv, &endptr, 0); + if (*endptr) { + err("can't parse %s as ID\n", **argv); + return -1; + } + NEXT_ARGP(); + + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0) + err("get by id (%u): %s\n", id, strerror(errno)); + return fd; + } else if (is_prefix(**argv, "tag")) { + unsigned char tag[BPF_TAG_SIZE]; + + NEXT_ARGP(); + + if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, + tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) + != BPF_TAG_SIZE) { + err("can't parse tag\n"); + return -1; + } + NEXT_ARGP(); + + return prog_fd_by_tag(tag); + } else if (is_prefix(**argv, "pinned")) { + char *path; + + NEXT_ARGP(); + + path = **argv; + NEXT_ARGP(); + + return open_obj_pinned_any(path, BPF_OBJ_PROG); + } + + err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv); + return -1; +} + +static void show_prog_maps(int fd, u32 num_maps) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + __u32 map_ids[num_maps]; + unsigned int i; + int err; + + info.nr_map_ids = num_maps; + info.map_ids = ptr_to_u64(map_ids); + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err || !info.nr_map_ids) + return; + + printf(" map_ids "); + for (i = 0; i < info.nr_map_ids; i++) + printf("%u%s", map_ids[i], + i == info.nr_map_ids - 1 ? "" : ","); +} + +static int show_prog(int fd) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + char *memlock; + int err; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) { + err("can't get prog info: %s\n", strerror(errno)); + return -1; + } + + printf("%u: ", info.id); + if (info.type < ARRAY_SIZE(prog_type_name)) + printf("%s ", prog_type_name[info.type]); + else + printf("type %u ", info.type); + + if (*info.name) + printf("name %s ", info.name); + + printf("tag "); + print_hex(info.tag, BPF_TAG_SIZE, ":"); + printf("\n"); + + if (info.load_time) { + char buf[32]; + + print_boot_time(info.load_time, buf, sizeof(buf)); + + /* Piggy back on load_time, since 0 uid is a valid one */ + printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid); + } + + printf("\txlated %uB", info.xlated_prog_len); + + if (info.jited_prog_len) + printf(" jited %uB", info.jited_prog_len); + else + printf(" not jited"); + + memlock = get_fdinfo(fd, "memlock"); + if (memlock) + printf(" memlock %sB", memlock); + free(memlock); + + if (info.nr_map_ids) + show_prog_maps(fd, info.nr_map_ids); + + printf("\n"); + + return 0; +} + +static int do_show(int argc, char **argv) +{ __u32 id = 0; + int err; + int fd; + + if (argc == 2) { + fd = prog_parse_fd(&argc, &argv); + if (fd < 0) + return -1; + + return show_prog(fd); + } + + if (argc) + return BAD_ARG(); + + while (true) { + err = bpf_prog_get_next_id(id, &id); + if (err) { + if (errno == ENOENT) + break; + err("can't get next program: %s\n", strerror(errno)); + if (errno == EINVAL) + err("kernel too old?\n"); + return -1; + } + + fd = bpf_prog_get_fd_by_id(id); + if (fd < 0) { + err("can't get prog by id (%u): %s\n", + id, strerror(errno)); + return -1; + } + + err = show_prog(fd); + close(fd); + if (err) + return err; + } + + return 0; +} + +static int do_dump(int argc, char **argv) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + bool can_disasm = false; + unsigned int buf_size; + char *filepath = NULL; + bool opcodes = false; + unsigned char *buf; + __u32 *member_len; + __u64 *member_ptr; + ssize_t n; + int err; + int fd; + + if (is_prefix(*argv, "jited")) { + member_len = &info.jited_prog_len; + member_ptr = &info.jited_prog_insns; + can_disasm = true; + } else if (is_prefix(*argv, "xlated")) { + member_len = &info.xlated_prog_len; + member_ptr = &info.xlated_prog_insns; + } else { + err("expected 'xlated' or 'jited', got: %s\n", *argv); + return -1; + } + NEXT_ARG(); + + if (argc < 2) + usage(); + + fd = prog_parse_fd(&argc, &argv); + if (fd < 0) + return -1; + + if (is_prefix(*argv, "file")) { + NEXT_ARG(); + if (!argc) { + err("expected file path\n"); + return -1; + } + + filepath = *argv; + NEXT_ARG(); + } else if (is_prefix(*argv, "opcodes")) { + opcodes = true; + NEXT_ARG(); + } + + if (!filepath && !can_disasm) { + err("expected 'file' got %s\n", *argv); + return -1; + } + if (argc) { + usage(); + return -1; + } + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) { + err("can't get prog info: %s\n", strerror(errno)); + return -1; + } + + if (!*member_len) { + info("no instructions returned\n"); + close(fd); + return 0; + } + + buf_size = *member_len; + + buf = malloc(buf_size); + if (!buf) { + err("mem alloc failed\n"); + close(fd); + return -1; + } + + memset(&info, 0, sizeof(info)); + + *member_ptr = ptr_to_u64(buf); + *member_len = buf_size; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + close(fd); + if (err) { + err("can't get prog info: %s\n", strerror(errno)); + goto err_free; + } + + if (*member_len > buf_size) { + info("too many instructions returned\n"); + goto err_free; + } + + if (filepath) { + fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + err("can't open file %s: %s\n", filepath, + strerror(errno)); + goto err_free; + } + + n = write(fd, buf, *member_len); + close(fd); + if (n != *member_len) { + err("error writing output file: %s\n", + n < 0 ? strerror(errno) : "short write"); + goto err_free; + } + } else { + disasm_print_insn(buf, *member_len, opcodes); + } + + free(buf); + + return 0; + +err_free: + free(buf); + return -1; +} + +static int do_pin(int argc, char **argv) +{ + return do_pin_any(argc, argv, bpf_prog_get_fd_by_id); +} + +static int do_help(int argc, char **argv) +{ + fprintf(stderr, + "Usage: %s %s show [PROG]\n" + " %s %s dump xlated PROG file FILE\n" + " %s %s dump jited PROG [file FILE] [opcodes]\n" + " %s %s pin PROG FILE\n" + " %s %s help\n" + "\n" + " " HELP_SPEC_PROGRAM "\n" + "", + bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], + bin_name, argv[-2], bin_name, argv[-2]); + + return 0; +} + +static const struct cmd cmds[] = { + { "show", do_show }, + { "dump", do_dump }, + { "pin", do_pin }, + { 0 } +}; + +int do_prog(int argc, char **argv) +{ + return cmd_select(cmds, argc, argv, do_help); +} -- cgit v1.2.3 From c9c35995bcf812ee8136f634c25bc6ccc3021d4c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Oct 2017 10:30:13 -0700 Subject: tools: bpftool: use the kernel's instruction printer Compile the instruction printer from kernel/bpf and use it for disassembling "translated" eBPF code. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 11 +++--- tools/bpf/bpftool/Makefile | 7 ++-- tools/bpf/bpftool/main.h | 10 ++---- tools/bpf/bpftool/prog.c | 44 +++++++++++++++++++----- 4 files changed, 51 insertions(+), 21 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 57fc4b9924ea..04d12f768f06 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -11,7 +11,7 @@ SYNOPSIS ======== | **bpftool** prog show [*PROG*] -| **bpftool** prog dump xlated *PROG* file *FILE* +| **bpftool** prog dump xlated *PROG* [file *FILE*] [opcodes] | **bpftool** prog dump jited *PROG* [file *FILE*] [opcodes] | **bpftool** prog pin *PROG* *FILE* | **bpftool** prog help @@ -28,9 +28,12 @@ DESCRIPTION Output will start with program ID followed by program type and zero or more named attributes (depending on kernel version). - **bpftool prog dump xlated** *PROG* **file** *FILE* - Dump eBPF instructions of the program from the kernel to a - file. + **bpftool prog dump xlated** *PROG* [**file** *FILE*] [**opcodes**] + Dump eBPF instructions of the program from the kernel. + If *FILE* is specified image will be written to a file, + otherwise it will be disassembled and printed to stdout. + + **opcodes** controls if raw opcodes will be printed. **bpftool prog dump jited** *PROG* [**file** *FILE*] [**opcodes**] Dump jited image (host machine code) of the program. diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 8705ee44664d..4f339824ca57 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -51,7 +51,7 @@ CC = gcc CFLAGS += -O2 CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow -CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf +CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf -I$(srctree)/kernel/bpf/ LIBS = -lelf -lbfd -lopcodes $(LIBBPF) include $(wildcard *.d) @@ -59,7 +59,10 @@ include $(wildcard *.d) all: $(OUTPUT)bpftool SRCS=$(wildcard *.c) -OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) +OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o + +$(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c + $(QUIET_CC)$(COMPILE.c) -MMD -o $@ $< $(OUTPUT)bpftool: $(OBJS) $(LIBBPF) $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS) diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 85d2d7870a58..8e809b2bb311 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -36,11 +36,12 @@ #ifndef __BPF_TOOL_H #define __BPF_TOOL_H +/* BFD and kernel.h both define GCC_VERSION, differently */ +#undef GCC_VERSION #include #include #include - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#include #define err(msg...) fprintf(stderr, "Error: " msg) #define warn(msg...) fprintf(stderr, "Warning: " msg) @@ -48,11 +49,6 @@ #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) -#define min(a, b) \ - ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _b : _a; }) -#define max(a, b) \ - ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _b : _a; }) - #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 421ba89ce86a..9e2681c83717 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include "main.h" +#include "disasm.h" static const char * const prog_type_name[] = { [BPF_PROG_TYPE_UNSPEC] = "unspec", @@ -297,11 +299,39 @@ static int do_show(int argc, char **argv) return 0; } +static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +static void dump_xlated(void *buf, unsigned int len, bool opcodes) +{ + struct bpf_insn *insn = buf; + unsigned int i; + + for (i = 0; i < len / sizeof(*insn); i++) { + printf("% 4d: ", i); + print_bpf_insn(print_insn, NULL, insn + i, true); + + if (opcodes) { + printf(" "); + print_hex(insn + i, 8, " "); + printf("\n"); + } + + if (insn[i].code == (BPF_LD | BPF_IMM | BPF_DW)) + i++; + } +} + static int do_dump(int argc, char **argv) { struct bpf_prog_info info = {}; __u32 len = sizeof(info); - bool can_disasm = false; unsigned int buf_size; char *filepath = NULL; bool opcodes = false; @@ -315,7 +345,6 @@ static int do_dump(int argc, char **argv) if (is_prefix(*argv, "jited")) { member_len = &info.jited_prog_len; member_ptr = &info.jited_prog_insns; - can_disasm = true; } else if (is_prefix(*argv, "xlated")) { member_len = &info.xlated_prog_len; member_ptr = &info.xlated_prog_insns; @@ -346,10 +375,6 @@ static int do_dump(int argc, char **argv) NEXT_ARG(); } - if (!filepath && !can_disasm) { - err("expected 'file' got %s\n", *argv); - return -1; - } if (argc) { usage(); return -1; @@ -409,7 +434,10 @@ static int do_dump(int argc, char **argv) goto err_free; } } else { - disasm_print_insn(buf, *member_len, opcodes); + if (member_len == &info.jited_prog_len) + disasm_print_insn(buf, *member_len, opcodes); + else + dump_xlated(buf, *member_len, opcodes); } free(buf); @@ -430,7 +458,7 @@ static int do_help(int argc, char **argv) { fprintf(stderr, "Usage: %s %s show [PROG]\n" - " %s %s dump xlated PROG file FILE\n" + " %s %s dump xlated PROG [file FILE] [opcodes]\n" " %s %s dump jited PROG [file FILE] [opcodes]\n" " %s %s pin PROG FILE\n" " %s %s help\n" -- cgit v1.2.3 From 2dc7c1fef9565c73c5054fd3c134afada09476c1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 16 Oct 2017 10:12:54 -0700 Subject: tools: bpftool: use more common tag format Program tag is usually displayed as string of bytes without any separators (e.g. as "aa5520b1090cfeb6" vs MAC addr-like format bpftool uses currently: "aa:55:20:b1:09:0c:fe:b6"). Make bptfool use the more common format both for displaying the tag and selecting the program by tag. This was pointed out in review but I misunderstood the comment. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 2 +- tools/bpf/bpftool/main.h | 3 +-- tools/bpf/bpftool/prog.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 04d12f768f06..3968f0bd37db 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -55,7 +55,7 @@ EXAMPLES **# bpftool prog show** :: - 10: xdp name some_prog tag 00:5a:3d:21:23:62:0c:8b + 10: xdp name some_prog tag 005a3d2123620c8b loaded_at Sep 29/20:11 uid 0 xlated 528B jited 370B memlock 4096B map_ids 10 diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 8e809b2bb311..844e4ef6db56 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -53,8 +53,7 @@ #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) -#define BPF_TAG_FMT "%02hhx:%02hhx:%02hhx:%02hhx:" \ - "%02hhx:%02hhx:%02hhx:%02hhx" +#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define HELP_SPEC_PROGRAM \ "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 9e2681c83717..d60f5307b6e2 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -224,7 +224,7 @@ static int show_prog(int fd) printf("name %s ", info.name); printf("tag "); - print_hex(info.tag, BPF_TAG_SIZE, ":"); + print_hex(info.tag, BPF_TAG_SIZE, ""); printf("\n"); if (info.load_time) { -- cgit v1.2.3 From 9cbe1f581d17baff7e93936feb041c90b29eb6a8 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:19 -0700 Subject: tools: bpftool: add pointer to file argument to print_hex() Make print_hex() able to print to any file instead of standard output only, and rename it to fprint_hex(). The function can now be called with the info() macro, for example, without splitting the output between standard and error outputs. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/main.c | 8 ++++---- tools/bpf/bpftool/main.h | 2 +- tools/bpf/bpftool/map.c | 20 ++++++++++---------- tools/bpf/bpftool/prog.c | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index e02d00d6e00b..8662199ee050 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -100,7 +100,7 @@ bool is_prefix(const char *pfx, const char *str) return !memcmp(str, pfx, strlen(pfx)); } -void print_hex(void *arg, unsigned int n, const char *sep) +void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) { unsigned char *data = arg; unsigned int i; @@ -111,13 +111,13 @@ void print_hex(void *arg, unsigned int n, const char *sep) if (!i) /* nothing */; else if (!(i % 16)) - printf("\n"); + fprintf(f, "\n"); else if (!(i % 8)) - printf(" "); + fprintf(f, " "); else pfx = sep; - printf("%s%02hhx", i ? pfx : "", data[i]); + fprintf(f, "%s%02hhx", i ? pfx : "", data[i]); } } diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 844e4ef6db56..41e6c7d3fcad 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -67,7 +67,7 @@ enum bpf_obj_type { extern const char *bin_name; bool is_prefix(const char *pfx, const char *str); -void print_hex(void *arg, unsigned int n, const char *sep); +void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __attribute__((noreturn)); struct cmd { diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 0528a5379e6c..b1dad76215ed 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -216,12 +216,12 @@ static void print_entry(struct bpf_map_info *info, unsigned char *key, !break_names; printf("key:%c", break_names ? '\n' : ' '); - print_hex(key, info->key_size, " "); + fprint_hex(stdout, key, info->key_size, " "); printf(single_line ? " " : "\n"); printf("value:%c", break_names ? '\n' : ' '); - print_hex(value, info->value_size, " "); + fprint_hex(stdout, value, info->value_size, " "); printf("\n"); } else { @@ -230,13 +230,13 @@ static void print_entry(struct bpf_map_info *info, unsigned char *key, n = get_possible_cpus(); printf("key:\n"); - print_hex(key, info->key_size, " "); + fprint_hex(stdout, key, info->key_size, " "); printf("\n"); for (i = 0; i < n; i++) { printf("value (CPU %02d):%c", i, info->value_size > 16 ? '\n' : ' '); - print_hex(value + i * info->value_size, - info->value_size, " "); + fprint_hex(stdout, value + i * info->value_size, + info->value_size, " "); printf("\n"); } } @@ -492,8 +492,8 @@ static int do_dump(int argc, char **argv) print_entry(&info, key, value); } else { info("can't lookup element with key: "); - print_hex(key, info.key_size, " "); - printf("\n"); + fprint_hex(stderr, key, info.key_size, " "); + fprintf(stderr, "\n"); } prev_key = key; @@ -587,7 +587,7 @@ static int do_lookup(int argc, char **argv) print_entry(&info, key, value); } else if (errno == ENOENT) { printf("key:\n"); - print_hex(key, info.key_size, " "); + fprint_hex(stdout, key, info.key_size, " "); printf("\n\nNot found\n"); } else { err("lookup failed: %s\n", strerror(errno)); @@ -642,14 +642,14 @@ static int do_getnext(int argc, char **argv) if (key) { printf("key:\n"); - print_hex(key, info.key_size, " "); + fprint_hex(stdout, key, info.key_size, " "); printf("\n"); } else { printf("key: None\n"); } printf("next key:\n"); - print_hex(nextkey, info.key_size, " "); + fprint_hex(stdout, nextkey, info.key_size, " "); printf("\n"); exit_free: diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index d60f5307b6e2..aa6d72ea3807 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -224,7 +224,7 @@ static int show_prog(int fd) printf("name %s ", info.name); printf("tag "); - print_hex(info.tag, BPF_TAG_SIZE, ""); + fprint_hex(stdout, info.tag, BPF_TAG_SIZE, ""); printf("\n"); if (info.load_time) { @@ -319,7 +319,7 @@ static void dump_xlated(void *buf, unsigned int len, bool opcodes) if (opcodes) { printf(" "); - print_hex(insn + i, 8, " "); + fprint_hex(stdout, insn + i, 8, " "); printf("\n"); } -- cgit v1.2.3 From 1739c26da72c4170c86c368c75133adbb740efef Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:20 -0700 Subject: tools: bpftool: fix return value when all eBPF programs have been shown Change the program to have a more consistent return code. Specifically, do not make bpftool return an error code simply because it reaches the end of the list of the eBPF programs to show. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index aa6d72ea3807..ede7957adcd9 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -275,8 +275,10 @@ static int do_show(int argc, char **argv) while (true) { err = bpf_prog_get_next_id(id, &id); if (err) { - if (errno == ENOENT) + if (errno == ENOENT) { + err = 0; break; + } err("can't get next program: %s\n", strerror(errno)); if (errno == EINVAL) err("kernel too old?\n"); -- cgit v1.2.3 From 1d84487e2a2b98892c3dec7934604e9b76577aa6 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:21 -0700 Subject: tools: bpftool: use err() instead of info() if there are too many insns Make error messages and return codes more consistent. Specifically, replace the use of info() macro with err() when too many eBPF instructions are received to be dumped, given that bpftool returns with a non-null exit value in that case. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index ede7957adcd9..6c03d2ea3f79 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -416,7 +416,7 @@ static int do_dump(int argc, char **argv) } if (*member_len > buf_size) { - info("too many instructions returned\n"); + err("too many instructions returned\n"); goto err_free; } -- cgit v1.2.3 From 9f606179c84930ada1e347b6d84bb913c8492fec Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:22 -0700 Subject: tools: bpftool: add `bpftool prog help` as real command i.r.t exit code Make error messages and return codes more consistent. Specifically, make `bpftool prog help` a real command, instead of printing usage by default for a non-recognized "help" command. Output is the same, but this makes bpftool return with a success value instead of an error. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 6c03d2ea3f79..355c14325622 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -475,6 +475,7 @@ static int do_help(int argc, char **argv) static const struct cmd cmds[] = { { "show", do_show }, + { "help", do_help }, { "dump", do_dump }, { "pin", do_pin }, { 0 } -- cgit v1.2.3 From 9e2308c133a92ff98d1397149c8483858bcf8fc0 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:24 -0700 Subject: tools: bpftool: print all relevant byte opcodes for "load double word" The eBPF instruction permitting to load double words (8 bytes) into a register need 8-byte long "immediate" field, and thus occupy twice the space of other instructions. bpftool was aware of this and would increment the instruction counter only once on meeting such instruction, but it would only print the first four bytes of the immediate value to load. Make it able to dump the whole 16 byte-long double instruction instead (as would `llvm-objdump -d `). Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 355c14325622..57edbea2fbe8 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -313,20 +313,29 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) static void dump_xlated(void *buf, unsigned int len, bool opcodes) { struct bpf_insn *insn = buf; + bool double_insn = false; unsigned int i; for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = false; + continue; + } + + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + printf("% 4d: ", i); print_bpf_insn(print_insn, NULL, insn + i, true); if (opcodes) { printf(" "); fprint_hex(stdout, insn + i, 8, " "); + if (double_insn && i < len - 1) { + printf(" "); + fprint_hex(stdout, insn + i + 1, 8, " "); + } printf("\n"); } - - if (insn[i].code == (BPF_LD | BPF_IMM | BPF_DW)) - i++; } } -- cgit v1.2.3 From 8dfbc6d1d213df340e5dcfdcdc76ad9407a29273 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Thu, 19 Oct 2017 15:46:25 -0700 Subject: tools: bpftool: show that `opcodes` or `file FILE` should be exclusive For the `bpftool prog dump { jited | xlated } ...` command, adding `opcodes` keyword (to request opcodes to be printed) will have no effect if `file FILE` (to write binary output to FILE) is provided. The manual page and the help message to be displayed in the terminal should reflect that, and indicate that these options should be mutually exclusive. Signed-off-by: Quentin Monnet Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 8 ++++---- tools/bpf/bpftool/prog.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 3968f0bd37db..69b3770370c8 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -11,8 +11,8 @@ SYNOPSIS ======== | **bpftool** prog show [*PROG*] -| **bpftool** prog dump xlated *PROG* [file *FILE*] [opcodes] -| **bpftool** prog dump jited *PROG* [file *FILE*] [opcodes] +| **bpftool** prog dump xlated *PROG* [{file *FILE* | opcodes }] +| **bpftool** prog dump jited *PROG* [{file *FILE* | opcodes }] | **bpftool** prog pin *PROG* *FILE* | **bpftool** prog help | @@ -28,14 +28,14 @@ DESCRIPTION Output will start with program ID followed by program type and zero or more named attributes (depending on kernel version). - **bpftool prog dump xlated** *PROG* [**file** *FILE*] [**opcodes**] + **bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** }] Dump eBPF instructions of the program from the kernel. If *FILE* is specified image will be written to a file, otherwise it will be disassembled and printed to stdout. **opcodes** controls if raw opcodes will be printed. - **bpftool prog dump jited** *PROG* [**file** *FILE*] [**opcodes**] + **bpftool prog dump jited** *PROG* [{ **file** *FILE* | **opcodes** }] Dump jited image (host machine code) of the program. If *FILE* is specified image will be written to a file, otherwise it will be disassembled and printed to stdout. diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 57edbea2fbe8..7838206a455b 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -469,8 +469,8 @@ static int do_help(int argc, char **argv) { fprintf(stderr, "Usage: %s %s show [PROG]\n" - " %s %s dump xlated PROG [file FILE] [opcodes]\n" - " %s %s dump jited PROG [file FILE] [opcodes]\n" + " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" + " %s %s dump jited PROG [{ file FILE | opcodes }]\n" " %s %s pin PROG FILE\n" " %s %s help\n" "\n" -- cgit v1.2.3 From 743cc665d5f62d2c75eceb59c461e653ad6ea58c Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:08 -0700 Subject: tools: bpftool: add JSON output for `bpftool prog show *` command Reuse the json_writer API introduced in an earlier commit to make bpftool able to generate JSON output on `bpftool prog show *` commands. For readability, the code from show_prog() has been split into two functions, one for plain output, one for JSON. Outputs from sample programs have been successfully tested against a JSON validator. Signed-off-by: Quentin Monnet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 139 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 32 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 7838206a455b..f373f2baef5a 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -195,51 +195,100 @@ static void show_prog_maps(int fd, u32 num_maps) if (err || !info.nr_map_ids) return; - printf(" map_ids "); - for (i = 0; i < info.nr_map_ids; i++) - printf("%u%s", map_ids[i], - i == info.nr_map_ids - 1 ? "" : ","); + if (json_output) { + jsonw_name(json_wtr, "map_ids"); + jsonw_start_array(json_wtr); + for (i = 0; i < info.nr_map_ids; i++) + jsonw_uint(json_wtr, map_ids[i]); + jsonw_end_array(json_wtr); + } else { + printf(" map_ids "); + for (i = 0; i < info.nr_map_ids; i++) + printf("%u%s", map_ids[i], + i == info.nr_map_ids - 1 ? "" : ","); + } } -static int show_prog(int fd) +static void print_prog_json(struct bpf_prog_info *info, int fd) { - struct bpf_prog_info info = {}; - __u32 len = sizeof(info); char *memlock; - int err; - err = bpf_obj_get_info_by_fd(fd, &info, &len); - if (err) { - err("can't get prog info: %s\n", strerror(errno)); - return -1; + jsonw_start_object(json_wtr); + jsonw_uint_field(json_wtr, "id", info->id); + if (info->type < ARRAY_SIZE(prog_type_name)) + jsonw_string_field(json_wtr, "type", + prog_type_name[info->type]); + else + jsonw_uint_field(json_wtr, "type", info->type); + + if (*info->name) + jsonw_string_field(json_wtr, "name", info->name); + + jsonw_name(json_wtr, "tag"); + jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", + info->tag[0], info->tag[1], info->tag[2], info->tag[3], + info->tag[4], info->tag[5], info->tag[6], info->tag[7]); + + if (info->load_time) { + char buf[32]; + + print_boot_time(info->load_time, buf, sizeof(buf)); + + /* Piggy back on load_time, since 0 uid is a valid one */ + jsonw_string_field(json_wtr, "loaded_at", buf); + jsonw_uint_field(json_wtr, "uid", info->created_by_uid); } - printf("%u: ", info.id); - if (info.type < ARRAY_SIZE(prog_type_name)) - printf("%s ", prog_type_name[info.type]); + jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); + + if (info->jited_prog_len) { + jsonw_bool_field(json_wtr, "jited", true); + jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); + } else { + jsonw_bool_field(json_wtr, "jited", false); + } + + memlock = get_fdinfo(fd, "memlock"); + if (memlock) + jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); + free(memlock); + + if (info->nr_map_ids) + show_prog_maps(fd, info->nr_map_ids); + + jsonw_end_object(json_wtr); +} + +static void print_prog_plain(struct bpf_prog_info *info, int fd) +{ + char *memlock; + + printf("%u: ", info->id); + if (info->type < ARRAY_SIZE(prog_type_name)) + printf("%s ", prog_type_name[info->type]); else - printf("type %u ", info.type); + printf("type %u ", info->type); - if (*info.name) - printf("name %s ", info.name); + if (*info->name) + printf("name %s ", info->name); printf("tag "); - fprint_hex(stdout, info.tag, BPF_TAG_SIZE, ""); + fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); printf("\n"); - if (info.load_time) { + if (info->load_time) { char buf[32]; - print_boot_time(info.load_time, buf, sizeof(buf)); + print_boot_time(info->load_time, buf, sizeof(buf)); /* Piggy back on load_time, since 0 uid is a valid one */ - printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid); + printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); } - printf("\txlated %uB", info.xlated_prog_len); + printf("\txlated %uB", info->xlated_prog_len); - if (info.jited_prog_len) - printf(" jited %uB", info.jited_prog_len); + if (info->jited_prog_len) + printf(" jited %uB", info->jited_prog_len); else printf(" not jited"); @@ -248,16 +297,35 @@ static int show_prog(int fd) printf(" memlock %sB", memlock); free(memlock); - if (info.nr_map_ids) - show_prog_maps(fd, info.nr_map_ids); + if (info->nr_map_ids) + show_prog_maps(fd, info->nr_map_ids); printf("\n"); +} + +static int show_prog(int fd) +{ + struct bpf_prog_info info = {}; + __u32 len = sizeof(info); + int err; + + err = bpf_obj_get_info_by_fd(fd, &info, &len); + if (err) { + err("can't get prog info: %s\n", strerror(errno)); + return -1; + } + + if (json_output) + print_prog_json(&info, fd); + else + print_prog_plain(&info, fd); return 0; } static int do_show(int argc, char **argv) -{ __u32 id = 0; +{ + __u32 id = 0; int err; int fd; @@ -272,6 +340,8 @@ static int do_show(int argc, char **argv) if (argc) return BAD_ARG(); + if (json_output) + jsonw_start_array(json_wtr); while (true) { err = bpf_prog_get_next_id(id, &id); if (err) { @@ -282,23 +352,28 @@ static int do_show(int argc, char **argv) err("can't get next program: %s\n", strerror(errno)); if (errno == EINVAL) err("kernel too old?\n"); - return -1; + err = -1; + break; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { err("can't get prog by id (%u): %s\n", id, strerror(errno)); - return -1; + err = -1; + break; } err = show_prog(fd); close(fd); if (err) - return err; + break; } - return 0; + if (json_output) + jsonw_end_array(json_wtr); + + return err; } static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) -- cgit v1.2.3 From f05e2c32f715985f54265b1e237b5cce1b576c71 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:10 -0700 Subject: tools: bpftool: add JSON output for `bpftool prog dump xlated *` command Add a new printing function to dump translated eBPF instructions as JSON. As for plain output, opcodes are printed only on request (when `opcodes` is provided on the command line). The disassembled output is generated by the same code that is used by the kernel verifier. Example output: $ bpftool --json --pretty prog dump xlated id 1 [{ "disasm": "(bf) r6 = r1" },{ "disasm": "(61) r7 = *(u32 *)(r6 +16)" },{ "disasm": "(95) exit" } ] $ bpftool --json --pretty prog dump xlated id 1 opcodes [{ "disasm": "(bf) r6 = r1", "opcodes": { "code": "0xbf", "src_reg": "0x1", "dst_reg": "0x6", "off": ["0x00","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } },{ "disasm": "(61) r7 = *(u32 *)(r6 +16)", "opcodes": { "code": "0x61", "src_reg": "0x6", "dst_reg": "0x7", "off": ["0x10","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } },{ "disasm": "(95) exit", "opcodes": { "code": "0x95", "src_reg": "0x0", "dst_reg": "0x0", "off": ["0x00","0x00" ], "imm": ["0x00","0x00","0x00","0x00" ] } } ] Signed-off-by: Quentin Monnet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 10 ++++++ tools/bpf/bpftool/json_writer.c | 8 +++++ tools/bpf/bpftool/json_writer.h | 2 ++ tools/bpf/bpftool/main.h | 1 + tools/bpf/bpftool/prog.c | 70 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 2 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index df8396a0c400..e7a756b8ee21 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -214,3 +214,13 @@ char *get_fdinfo(int fd, const char *key) fclose(fdi); return NULL; } + +void print_hex_data_json(uint8_t *data, size_t len) +{ + unsigned int i; + + jsonw_start_array(json_wtr); + for (i = 0; i < len; i++) + jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]); + jsonw_end_array(json_wtr); +} diff --git a/tools/bpf/bpftool/json_writer.c b/tools/bpf/bpftool/json_writer.c index 6b77d288cce2..c6eef76322ae 100644 --- a/tools/bpf/bpftool/json_writer.c +++ b/tools/bpf/bpftool/json_writer.c @@ -156,6 +156,14 @@ void jsonw_name(json_writer_t *self, const char *name) putc(' ', self->out); } +void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap) +{ + jsonw_eor(self); + putc('"', self->out); + vfprintf(self->out, fmt, ap); + putc('"', self->out); +} + void jsonw_printf(json_writer_t *self, const char *fmt, ...) { va_list ap; diff --git a/tools/bpf/bpftool/json_writer.h b/tools/bpf/bpftool/json_writer.h index 1516aafba59d..0fa2fb1b6351 100644 --- a/tools/bpf/bpftool/json_writer.h +++ b/tools/bpf/bpftool/json_writer.h @@ -17,6 +17,7 @@ #include #include +#include /* Opaque class structure */ typedef struct json_writer json_writer_t; @@ -33,6 +34,7 @@ void jsonw_pretty(json_writer_t *self, bool on); void jsonw_name(json_writer_t *self, const char *name); /* Add value */ +void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap); void jsonw_printf(json_writer_t *self, const char *fmt, ...); void jsonw_string(json_writer_t *self, const char *value); void jsonw_bool(json_writer_t *self, bool value); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 15927fc9fb31..693fc9710be1 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -95,5 +95,6 @@ int do_map(int argc, char **arg); int prog_parse_fd(int *argc, char ***argv); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); +void print_hex_data_json(uint8_t *data, size_t len); #endif diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index f373f2baef5a..43e49799a624 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -385,7 +385,7 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) va_end(args); } -static void dump_xlated(void *buf, unsigned int len, bool opcodes) +static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) { struct bpf_insn *insn = buf; bool double_insn = false; @@ -414,6 +414,69 @@ static void dump_xlated(void *buf, unsigned int len, bool opcodes) } } +static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) +{ + unsigned int l = strlen(fmt); + char chomped_fmt[l]; + va_list args; + + va_start(args, fmt); + if (l > 0) { + strncpy(chomped_fmt, fmt, l - 1); + chomped_fmt[l - 1] = '\0'; + } + jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); + va_end(args); +} + +static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) +{ + struct bpf_insn *insn = buf; + bool double_insn = false; + unsigned int i; + + jsonw_start_array(json_wtr); + for (i = 0; i < len / sizeof(*insn); i++) { + if (double_insn) { + double_insn = false; + continue; + } + double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); + + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "disasm"); + print_bpf_insn(print_insn_json, NULL, insn + i, true); + + if (opcodes) { + jsonw_name(json_wtr, "opcodes"); + jsonw_start_object(json_wtr); + + jsonw_name(json_wtr, "code"); + jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); + + jsonw_name(json_wtr, "src_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); + + jsonw_name(json_wtr, "dst_reg"); + jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); + + jsonw_name(json_wtr, "off"); + print_hex_data_json((uint8_t *)(&insn[i].off), 2); + + jsonw_name(json_wtr, "imm"); + if (double_insn && i < len - 1) + print_hex_data_json((uint8_t *)(&insn[i].imm), + 12); + else + print_hex_data_json((uint8_t *)(&insn[i].imm), + 4); + jsonw_end_object(json_wtr); + } + jsonw_end_object(json_wtr); + } + jsonw_end_array(json_wtr); +} + static int do_dump(int argc, char **argv) { struct bpf_prog_info info = {}; @@ -523,7 +586,10 @@ static int do_dump(int argc, char **argv) if (member_len == &info.jited_prog_len) disasm_print_insn(buf, *member_len, opcodes); else - dump_xlated(buf, *member_len, opcodes); + if (json_output) + dump_xlated_json(buf, *member_len, opcodes); + else + dump_xlated_plain(buf, *member_len, opcodes); } free(buf); -- cgit v1.2.3 From 9a5ab8bf1d6d16ef47fdf55dba1683ec00d751ad Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:13 -0700 Subject: tools: bpftool: turn err() and info() macros into functions Turn err() and info() macros into functions. In order to avoid naming conflicts with variables in the code, rename them as p_err() and p_info() respectively. The behavior of these functions is similar to the one of the macros for plain output. However, when JSON output is requested, these macros return a JSON-formatted "error" object instead of printing a message to stderr. To handle error messages correctly with JSON, a modification was brought to their behavior nonetheless: the functions now append a end-of-line character at the end of the message. This way, we can remove end-of-line characters at the end of the argument strings, and not have them in the JSON output. All error messages are formatted to hold in a single call to p_err(), in order to produce a single JSON field. Signed-off-by: Quentin Monnet Acked-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 26 +++++++------- tools/bpf/bpftool/main.c | 16 ++++----- tools/bpf/bpftool/main.h | 37 +++++++++++++++++--- tools/bpf/bpftool/map.c | 87 +++++++++++++++++++++++++--------------------- tools/bpf/bpftool/prog.c | 51 +++++++++++++-------------- 5 files changed, 126 insertions(+), 91 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index e7a756b8ee21..b2533f1cae3e 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -66,8 +66,8 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) fd = bpf_obj_get(path); if (fd < 0) { - err("bpf obj get (%s): %s\n", path, - errno == EACCES && !is_bpffs(dirname(path)) ? + p_err("bpf obj get (%s): %s", path, + errno == EACCES && !is_bpffs(dirname(path)) ? "directory not in bpf file system (bpffs)" : strerror(errno)); return -1; @@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) return type; } if (type != exp_type) { - err("incorrect object type: %s\n", get_fd_type_name(type)); + p_err("incorrect object type: %s", get_fd_type_name(type)); close(fd); return -1; } @@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) int fd; if (!is_prefix(*argv, "id")) { - err("expected 'id' got %s\n", *argv); + p_err("expected 'id' got %s", *argv); return -1; } NEXT_ARG(); id = strtoul(*argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", *argv); + p_err("can't parse %s as ID", *argv); return -1; } NEXT_ARG(); @@ -112,15 +112,15 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) fd = get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", id, strerror(errno)); + p_err("can't get prog by id (%u): %s", id, strerror(errno)); return -1; } err = bpf_obj_pin(fd, *argv); close(fd); if (err) { - err("can't pin the object (%s): %s\n", *argv, - errno == EACCES && !is_bpffs(dirname(*argv)) ? + p_err("can't pin the object (%s): %s", *argv, + errno == EACCES && !is_bpffs(dirname(*argv)) ? "directory not in bpf file system (bpffs)" : strerror(errno)); return -1; @@ -153,11 +153,11 @@ int get_fd_type(int fd) n = readlink(path, buf, sizeof(buf)); if (n < 0) { - err("can't read link type: %s\n", strerror(errno)); + p_err("can't read link type: %s", strerror(errno)); return -1; } if (n == sizeof(path)) { - err("can't read link type: path too long!\n"); + p_err("can't read link type: path too long!"); return -1; } @@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key) fdi = fopen(path, "r"); if (!fdi) { - err("can't open fdinfo: %s\n", strerror(errno)); + p_err("can't open fdinfo: %s", strerror(errno)); return NULL; } @@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key) value = strchr(line, '\t'); if (!value || !value[1]) { - err("malformed fdinfo!?\n"); + p_err("malformed fdinfo!?"); free(line); return NULL; } @@ -209,7 +209,7 @@ char *get_fdinfo(int fd, const char *key) return line; } - err("key '%s' not found in fdinfo\n", key); + p_err("key '%s' not found in fdinfo", key); free(line); fclose(fdi); return NULL; diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 71b01bf73912..9989a77fdc4a 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -158,20 +158,20 @@ static int do_batch(int argc, char **argv) int i; if (argc < 2) { - err("too few parameters for batch\n"); + p_err("too few parameters for batch"); return -1; } else if (!is_prefix(*argv, "file")) { - err("expected 'file', got: %s\n", *argv); + p_err("expected 'file', got: %s", *argv); return -1; } else if (argc > 2) { - err("too many parameters for batch\n"); + p_err("too many parameters for batch"); return -1; } NEXT_ARG(); fp = fopen(*argv, "r"); if (!fp) { - err("Can't open file (%s): %s\n", *argv, strerror(errno)); + p_err("Can't open file (%s): %s", *argv, strerror(errno)); return -1; } @@ -189,8 +189,8 @@ static int do_batch(int argc, char **argv) while (n_argv[n_argc]) { n_argc++; if (n_argc == ARRAY_SIZE(n_argv)) { - err("line %d has too many arguments, skip\n", - lines); + p_err("line %d has too many arguments, skip", + lines); n_argc = 0; break; } @@ -225,7 +225,7 @@ static int do_batch(int argc, char **argv) perror("reading batch file failed"); err = -1; } else { - info("processed %d lines\n", lines); + p_info("processed %d lines", lines); err = 0; } err_close: @@ -279,7 +279,7 @@ int main(int argc, char **argv) if (json_output) { json_wtr = jsonw_new(stdout); if (!json_wtr) { - err("failed to create JSON writer\n"); + p_err("failed to create JSON writer"); return -1; } jsonw_pretty(json_wtr, pretty_output); diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 693fc9710be1..04c88b55d8c7 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -45,15 +45,11 @@ #include "json_writer.h" -#define err(msg...) fprintf(stderr, "Error: " msg) -#define warn(msg...) fprintf(stderr, "Warning: " msg) -#define info(msg...) fprintf(stderr, msg) - #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) -#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) +#define BAD_ARG() ({ p_err("what is '%s'?\n", *argv); -1; }) #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" @@ -97,4 +93,35 @@ int prog_parse_fd(int *argc, char ***argv); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); void print_hex_data_json(uint8_t *data, size_t len); +static inline void p_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (json_output) { + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "error"); + jsonw_vprintf_enquote(json_wtr, fmt, ap); + jsonw_end_object(json_wtr); + } else { + fprintf(stderr, "Error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +static inline void p_info(const char *fmt, ...) +{ + va_list ap; + + if (json_output) + return; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + #endif diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 14d89bfabc66..86c128c433ba 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -81,19 +81,19 @@ static unsigned int get_possible_cpus(void) fd = open("/sys/devices/system/cpu/possible", O_RDONLY); if (fd < 0) { - err("can't open sysfs possible cpus\n"); + p_err("can't open sysfs possible cpus"); exit(-1); } n = read(fd, buf, sizeof(buf)); if (n < 2) { - err("can't read sysfs possible cpus\n"); + p_err("can't read sysfs possible cpus"); exit(-1); } close(fd); if (n == sizeof(buf)) { - err("read sysfs possible cpus overflow\n"); + p_err("read sysfs possible cpus overflow"); exit(-1); } @@ -161,14 +161,14 @@ static int map_parse_fd(int *argc, char ***argv) id = strtoul(**argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", **argv); + p_err("can't parse %s as ID", **argv); return -1; } NEXT_ARGP(); fd = bpf_map_get_fd_by_id(id); if (fd < 0) - err("get map by id (%u): %s\n", id, strerror(errno)); + p_err("get map by id (%u): %s", id, strerror(errno)); return fd; } else if (is_prefix(**argv, "pinned")) { char *path; @@ -181,7 +181,7 @@ static int map_parse_fd(int *argc, char ***argv) return open_obj_pinned_any(path, BPF_OBJ_MAP); } - err("expected 'id' or 'pinned', got: '%s'?\n", **argv); + p_err("expected 'id' or 'pinned', got: '%s'?", **argv); return -1; } @@ -197,7 +197,7 @@ map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) err = bpf_obj_get_info_by_fd(fd, info, info_len); if (err) { - err("can't get map info: %s\n", strerror(errno)); + p_err("can't get map info: %s", strerror(errno)); close(fd); return err; } @@ -288,14 +288,14 @@ static char **parse_bytes(char **argv, const char *name, unsigned char *val, while (i < n && argv[i]) { val[i] = strtoul(argv[i], &endptr, 0); if (*endptr) { - err("error parsing byte: %s\n", argv[i]); + p_err("error parsing byte: %s", argv[i]); return NULL; } i++; } if (i != n) { - err("%s expected %d bytes got %d\n", name, n, i); + p_err("%s expected %d bytes got %d", name, n, i); return NULL; } @@ -309,16 +309,16 @@ static int parse_elem(char **argv, struct bpf_map_info *info, if (!*argv) { if (!key && !value) return 0; - err("did not find %s\n", key ? "key" : "value"); + p_err("did not find %s", key ? "key" : "value"); return -1; } if (is_prefix(*argv, "key")) { if (!key) { if (key_size) - err("duplicate key\n"); + p_err("duplicate key"); else - err("unnecessary key\n"); + p_err("unnecessary key"); return -1; } @@ -333,9 +333,9 @@ static int parse_elem(char **argv, struct bpf_map_info *info, if (!value) { if (value_size) - err("duplicate value\n"); + p_err("duplicate value"); else - err("unnecessary value\n"); + p_err("unnecessary value"); return -1; } @@ -345,11 +345,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info, int argc = 2; if (value_size != 4) { - err("value smaller than 4B for map in map?\n"); + p_err("value smaller than 4B for map in map?"); return -1; } if (!argv[0] || !argv[1]) { - err("not enough value arguments for map in map\n"); + p_err("not enough value arguments for map in map"); return -1; } @@ -363,11 +363,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info, int argc = 2; if (value_size != 4) { - err("value smaller than 4B for map of progs?\n"); + p_err("value smaller than 4B for map of progs?"); return -1; } if (!argv[0] || !argv[1]) { - err("not enough value arguments for map of progs\n"); + p_err("not enough value arguments for map of progs"); return -1; } @@ -388,7 +388,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || is_prefix(*argv, "exist")) { if (!flags) { - err("flags specified multiple times: %s\n", *argv); + p_err("flags specified multiple times: %s", *argv); return -1; } @@ -403,7 +403,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info, value_size, NULL, value_fd); } - err("expected key or value, got: %s\n", *argv); + p_err("expected key or value, got: %s", *argv); return -1; } @@ -499,22 +499,21 @@ static int do_show(int argc, char **argv) if (err) { if (errno == ENOENT) break; - err("can't get next map: %s\n", strerror(errno)); - if (errno == EINVAL) - err("kernel too old?\n"); + p_err("can't get next map: %s%s", strerror(errno), + errno == EINVAL ? " -- kernel too old?" : ""); return -1; } fd = bpf_map_get_fd_by_id(id); if (fd < 0) { - err("can't get map by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get map by id (%u): %s", + id, strerror(errno)); return -1; } err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get map info: %s\n", strerror(errno)); + p_err("can't get map info: %s", strerror(errno)); close(fd); return -1; } @@ -547,7 +546,7 @@ static int do_dump(int argc, char **argv) return -1; if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { - err("Dumping maps of maps and program maps not supported\n"); + p_err("Dumping maps of maps and program maps not supported"); close(fd); return -1; } @@ -555,7 +554,7 @@ static int do_dump(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed\n"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -577,9 +576,19 @@ static int do_dump(int argc, char **argv) else print_entry_plain(&info, key, value); } else { - info("can't lookup element with key: "); - fprint_hex(stderr, key, info.key_size, " "); - fprintf(stderr, "\n"); + if (json_output) { + jsonw_name(json_wtr, "key"); + print_hex_data_json(key, info.key_size); + jsonw_name(json_wtr, "value"); + jsonw_start_object(json_wtr); + jsonw_string_field(json_wtr, "error", + "can't lookup element"); + jsonw_end_object(json_wtr); + } else { + p_info("can't lookup element with key: "); + fprint_hex(stderr, key, info.key_size, " "); + fprintf(stderr, "\n"); + } } prev_key = key; @@ -619,7 +628,7 @@ static int do_update(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -631,7 +640,7 @@ static int do_update(int argc, char **argv) err = bpf_map_update_elem(fd, key, value, flags); if (err) { - err("update failed: %s\n", strerror(errno)); + p_err("update failed: %s", strerror(errno)); goto exit_free; } @@ -663,7 +672,7 @@ static int do_lookup(int argc, char **argv) key = malloc(info.key_size); value = alloc_value(&info); if (!key || !value) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -687,7 +696,7 @@ static int do_lookup(int argc, char **argv) printf("\n\nNot found\n"); } } else { - err("lookup failed: %s\n", strerror(errno)); + p_err("lookup failed: %s", strerror(errno)); } exit_free: @@ -716,7 +725,7 @@ static int do_getnext(int argc, char **argv) key = malloc(info.key_size); nextkey = malloc(info.key_size); if (!key || !nextkey) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -733,7 +742,7 @@ static int do_getnext(int argc, char **argv) err = bpf_map_get_next_key(fd, key, nextkey); if (err) { - err("can't get next key: %s\n", strerror(errno)); + p_err("can't get next key: %s", strerror(errno)); goto exit_free; } @@ -786,7 +795,7 @@ static int do_delete(int argc, char **argv) key = malloc(info.key_size); if (!key) { - err("mem alloc failed"); + p_err("mem alloc failed"); err = -1; goto exit_free; } @@ -797,7 +806,7 @@ static int do_delete(int argc, char **argv) err = bpf_map_delete_elem(fd, key); if (err) - err("delete failed: %s\n", strerror(errno)); + p_err("delete failed: %s", strerror(errno)); exit_free: free(key); diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 43e49799a624..41bd5390b4fc 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -104,21 +104,21 @@ static int prog_fd_by_tag(unsigned char *tag) while (true) { err = bpf_prog_get_next_id(id, &id); if (err) { - err("%s\n", strerror(errno)); + p_err("%s", strerror(errno)); return -1; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog by id (%u): %s", + id, strerror(errno)); return -1; } err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog info (%u): %s", + id, strerror(errno)); close(fd); return -1; } @@ -142,14 +142,14 @@ int prog_parse_fd(int *argc, char ***argv) id = strtoul(**argv, &endptr, 0); if (*endptr) { - err("can't parse %s as ID\n", **argv); + p_err("can't parse %s as ID", **argv); return -1; } NEXT_ARGP(); fd = bpf_prog_get_fd_by_id(id); if (fd < 0) - err("get by id (%u): %s\n", id, strerror(errno)); + p_err("get by id (%u): %s", id, strerror(errno)); return fd; } else if (is_prefix(**argv, "tag")) { unsigned char tag[BPF_TAG_SIZE]; @@ -159,7 +159,7 @@ int prog_parse_fd(int *argc, char ***argv) if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) != BPF_TAG_SIZE) { - err("can't parse tag\n"); + p_err("can't parse tag"); return -1; } NEXT_ARGP(); @@ -176,7 +176,7 @@ int prog_parse_fd(int *argc, char ***argv) return open_obj_pinned_any(path, BPF_OBJ_PROG); } - err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv); + p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); return -1; } @@ -311,7 +311,7 @@ static int show_prog(int fd) err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); return -1; } @@ -349,17 +349,16 @@ static int do_show(int argc, char **argv) err = 0; break; } - err("can't get next program: %s\n", strerror(errno)); - if (errno == EINVAL) - err("kernel too old?\n"); + p_err("can't get next program: %s%s", strerror(errno), + errno == EINVAL ? " -- kernel too old?" : ""); err = -1; break; } fd = bpf_prog_get_fd_by_id(id); if (fd < 0) { - err("can't get prog by id (%u): %s\n", - id, strerror(errno)); + p_err("can't get prog by id (%u): %s", + id, strerror(errno)); err = -1; break; } @@ -498,7 +497,7 @@ static int do_dump(int argc, char **argv) member_len = &info.xlated_prog_len; member_ptr = &info.xlated_prog_insns; } else { - err("expected 'xlated' or 'jited', got: %s\n", *argv); + p_err("expected 'xlated' or 'jited', got: %s", *argv); return -1; } NEXT_ARG(); @@ -513,7 +512,7 @@ static int do_dump(int argc, char **argv) if (is_prefix(*argv, "file")) { NEXT_ARG(); if (!argc) { - err("expected file path\n"); + p_err("expected file path"); return -1; } @@ -531,12 +530,12 @@ static int do_dump(int argc, char **argv) err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); return -1; } if (!*member_len) { - info("no instructions returned\n"); + p_info("no instructions returned"); close(fd); return 0; } @@ -545,7 +544,7 @@ static int do_dump(int argc, char **argv) buf = malloc(buf_size); if (!buf) { - err("mem alloc failed\n"); + p_err("mem alloc failed"); close(fd); return -1; } @@ -558,28 +557,28 @@ static int do_dump(int argc, char **argv) err = bpf_obj_get_info_by_fd(fd, &info, &len); close(fd); if (err) { - err("can't get prog info: %s\n", strerror(errno)); + p_err("can't get prog info: %s", strerror(errno)); goto err_free; } if (*member_len > buf_size) { - err("too many instructions returned\n"); + p_err("too many instructions returned"); goto err_free; } if (filepath) { fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) { - err("can't open file %s: %s\n", filepath, - strerror(errno)); + p_err("can't open file %s: %s", filepath, + strerror(errno)); goto err_free; } n = write(fd, buf, *member_len); close(fd); if (n != *member_len) { - err("error writing output file: %s\n", - n < 0 ? strerror(errno) : "short write"); + p_err("error writing output file: %s", + n < 0 ? strerror(errno) : "short write"); goto err_free; } } else { -- cgit v1.2.3 From 004b45c0e51a8b6f20320181a946ba2d1bd3548b Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:14 -0700 Subject: tools: bpftool: provide JSON output for all possible commands As all commands can now return JSON output (possibly just a "null" value), output of `bpftool --json batch file FILE` should also be fully JSON compliant. Signed-off-by: Quentin Monnet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/main.c | 25 +++++++++++++++++++++---- tools/bpf/bpftool/map.c | 16 +++++++++++++++- tools/bpf/bpftool/prog.c | 12 +++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 9989a77fdc4a..55ba0a04c102 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -64,6 +64,11 @@ void usage(void) static int do_help(int argc, char **argv) { + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + fprintf(stderr, "Usage: %s OBJECT { COMMAND | help }\n" " %s batch file FILE\n" @@ -77,10 +82,22 @@ static int do_help(int argc, char **argv) static int do_version(int argc, char **argv) { - printf("%s v%d.%d.%d\n", bin_name, - LINUX_VERSION_CODE >> 16, - LINUX_VERSION_CODE >> 8 & 0xf, - LINUX_VERSION_CODE & 0xf); + unsigned int version[3]; + + version[0] = LINUX_VERSION_CODE >> 16; + version[1] = LINUX_VERSION_CODE >> 8 & 0xf; + version[2] = LINUX_VERSION_CODE & 0xf; + + if (json_output) { + jsonw_start_object(json_wtr); + jsonw_name(json_wtr, "version"); + jsonw_printf(json_wtr, "\"%u.%u.%u\"", + version[0], version[1], version[2]); + jsonw_end_object(json_wtr); + } else { + printf("%s v%u.%u.%u\n", bin_name, + version[0], version[1], version[2]); + } return 0; } diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index 86c128c433ba..a611f31f574f 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -651,6 +651,8 @@ exit_free: free(value); close(fd); + if (!err && json_output) + jsonw_null(json_wtr); return err; } @@ -812,16 +814,28 @@ exit_free: free(key); close(fd); + if (!err && json_output) + jsonw_null(json_wtr); return err; } static int do_pin(int argc, char **argv) { - return do_pin_any(argc, argv, bpf_map_get_fd_by_id); + int err; + + err = do_pin_any(argc, argv, bpf_map_get_fd_by_id); + if (!err && json_output) + jsonw_null(json_wtr); + return err; } static int do_help(int argc, char **argv) { + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + fprintf(stderr, "Usage: %s %s show [MAP]\n" " %s %s dump MAP\n" diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 41bd5390b4fc..e07f35ff80d1 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -602,11 +602,21 @@ err_free: static int do_pin(int argc, char **argv) { - return do_pin_any(argc, argv, bpf_prog_get_fd_by_id); + int err; + + err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); + if (!err && json_output) + jsonw_null(json_wtr); + return err; } static int do_help(int argc, char **argv) { + if (json_output) { + jsonw_null(json_wtr); + return 0; + } + fprintf(stderr, "Usage: %s %s show [PROG]\n" " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" -- cgit v1.2.3 From 0641c3c890d480abeb237b92a5ee4b99a22319c6 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Mon, 23 Oct 2017 09:24:16 -0700 Subject: tools: bpftool: update documentation for --json and --pretty usage Update the documentation to provide help about JSON output generation, and add an example in bpftool-prog manual page. Also reintroduce an example that was left aside when the tool was moved from GitHub to the kernel sources, in order to show how to mount the bpffs file system (to pin programs) inside the bpftool-prog manual page. Signed-off-by: Quentin Monnet Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/bpf/bpftool/Documentation/bpftool-map.rst | 11 ++++- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 61 +++++++++++++++++++++++- tools/bpf/bpftool/Documentation/bpftool.rst | 12 ++++- tools/bpf/bpftool/main.c | 6 ++- tools/bpf/bpftool/main.h | 2 + tools/bpf/bpftool/map.c | 1 + tools/bpf/bpftool/prog.c | 1 + 7 files changed, 88 insertions(+), 6 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index 3954b3ea4f26..abb9ee940b15 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -10,7 +10,9 @@ tool for inspection and simple manipulation of eBPF maps SYNOPSIS ======== - **bpftool** **map** *COMMAND* + **bpftool** [*OPTIONS*] **map** *COMMAND* + + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } *COMMANDS* := { **show** | **dump** | **update** | **lookup** | **getnext** | **delete** @@ -77,6 +79,13 @@ OPTIONS -v, --version Print version number (similar to **bpftool version**). + -j, --json + Generate JSON output. For commands that cannot produce JSON, this + option has no effect. + + -p, --pretty + Generate human-readable JSON output. Implies **-j**. + EXAMPLES ======== **# bpftool map show** diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 685a19e71fec..0f25d3c39e05 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -10,6 +10,16 @@ tool for inspection and simple manipulation of eBPF progs SYNOPSIS ======== + **bpftool** [*OPTIONS*] **prog** *COMMAND* + + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } + + *COMMANDS* := + { **show** | **dump xlated** | **dump jited** | **pin** | **help** } + +MAP COMMANDS +============= + | **bpftool** **prog show** [*PROG*] | **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}] | **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}] @@ -58,6 +68,13 @@ OPTIONS -v, --version Print version number (similar to **bpftool version**). + -j, --json + Generate JSON output. For commands that cannot produce JSON, this + option has no effect. + + -p, --pretty + Generate human-readable JSON output. Implies **-j**. + EXAMPLES ======== **# bpftool prog show** @@ -67,13 +84,33 @@ EXAMPLES loaded_at Sep 29/20:11 uid 0 xlated 528B jited 370B memlock 4096B map_ids 10 +**# bpftool --json --pretty prog show** + +:: + + { + "programs": [{ + "id": 10, + "type": "xdp", + "tag": "005a3d2123620c8b", + "loaded_at": "Sep 29/20:11", + "uid": 0, + "bytes_xlated": 528, + "jited": true, + "bytes_jited": 370, + "bytes_memlock": 4096, + "map_ids": [10 + ] + } + ] + } + | | **# bpftool prog dump xlated id 10 file /tmp/t** | **# ls -l /tmp/t** | -rw------- 1 root root 560 Jul 22 01:42 /tmp/t -| -| **# bpftool prog dum jited pinned /sys/fs/bpf/prog** +**# bpftool prog dum jited tag 005a3d2123620c8b** :: @@ -83,6 +120,26 @@ EXAMPLES sub $0x28,%rbp mov %rbx,0x0(%rbp) +| +| **# mount -t bpf none /sys/fs/bpf/** +| **# bpftool prog pin id 10 /sys/fs/bpf/prog** +| **# ls -l /sys/fs/bpf/** +| -rw------- 1 root root 0 Jul 22 01:43 prog + +**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes** + +:: + + push %rbp + 55 + mov %rsp,%rbp + 48 89 e5 + sub $0x228,%rsp + 48 81 ec 28 02 00 00 + sub $0x28,%rbp + 48 83 ed 28 + mov %rbx,0x0(%rbp) + 48 89 5d 00 SEE ALSO diff --git a/tools/bpf/bpftool/Documentation/bpftool.rst b/tools/bpf/bpftool/Documentation/bpftool.rst index 44e07799d54d..926c03d5a8da 100644 --- a/tools/bpf/bpftool/Documentation/bpftool.rst +++ b/tools/bpf/bpftool/Documentation/bpftool.rst @@ -10,7 +10,7 @@ tool for inspection and simple manipulation of eBPF programs and maps SYNOPSIS ======== - **bpftool** *OBJECT* { *COMMAND* | **help** } + **bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** } **bpftool** **batch file** *FILE* @@ -18,6 +18,9 @@ SYNOPSIS *OBJECT* := { **map** | **program** } + *OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** } + | { **-j** | **--json** } [{ **-p** | **--pretty** }] } + *MAP-COMMANDS* := { **show** | **dump** | **update** | **lookup** | **getnext** | **delete** | **pin** | **help** } @@ -41,6 +44,13 @@ OPTIONS -v, --version Print version number (similar to **bpftool version**). + -j, --json + Generate JSON output. For commands that cannot produce JSON, this + option has no effect. + + -p, --pretty + Generate human-readable JSON output. Implies **-j**. + SEE ALSO ======== **bpftool-map**\ (8), **bpftool-prog**\ (8) diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 55ba0a04c102..78d9afb74ef4 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -70,11 +70,13 @@ static int do_help(int argc, char **argv) } fprintf(stderr, - "Usage: %s OBJECT { COMMAND | help }\n" + "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" " %s batch file FILE\n" " %s version\n" "\n" - " OBJECT := { prog | map }\n", + " OBJECT := { prog | map }\n" + " " HELP_SPEC_OPTIONS "\n" + "", bin_name, bin_name, bin_name); return 0; diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 04c88b55d8c7..2f94bed03a8d 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -55,6 +55,8 @@ #define HELP_SPEC_PROGRAM \ "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" +#define HELP_SPEC_OPTIONS \ + "OPTIONS := { {-j|--json} [{-p|--pretty}] }" enum bpf_obj_type { BPF_OBJ_UNKNOWN, diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index a611f31f574f..e978ab23a77f 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -850,6 +850,7 @@ static int do_help(int argc, char **argv) " " HELP_SPEC_PROGRAM "\n" " VALUE := { BYTES | MAP | PROG }\n" " UPDATE_FLAGS := { any | exist | noexist }\n" + " " HELP_SPEC_OPTIONS "\n" "", bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index e07f35ff80d1..250f80fd46aa 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -625,6 +625,7 @@ static int do_help(int argc, char **argv) " %s %s help\n" "\n" " " HELP_SPEC_PROGRAM "\n" + " " HELP_SPEC_OPTIONS "\n" "", bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); -- cgit v1.2.3 From 928631e05495fa1f0e9775f555b94dbcbb4e2fb5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:19 -0700 Subject: bpftool: print program device bound info If program is bound to a device, print the name of the relevant interface or unknown if the netdev has since been removed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- tools/bpf/bpftool/prog.c | 31 +++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 7 +++++++ 2 files changed, 38 insertions(+) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 250f80fd46aa..d3ab808dc882 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -229,6 +230,21 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) info->tag[0], info->tag[1], info->tag[2], info->tag[3], info->tag[4], info->tag[5], info->tag[6], info->tag[7]); + if (info->status & BPF_PROG_STATUS_DEV_BOUND) { + jsonw_name(json_wtr, "dev"); + if (info->ifindex) { + char name[IF_NAMESIZE]; + + if (!if_indextoname(info->ifindex, name)) + jsonw_printf(json_wtr, "\"ifindex:%d\"", + info->ifindex); + else + jsonw_printf(json_wtr, "\"%s\"", name); + } else { + jsonw_printf(json_wtr, "\"unknown\""); + } + } + if (info->load_time) { char buf[32]; @@ -274,6 +290,21 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) printf("tag "); fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); + printf(" "); + + if (info->status & BPF_PROG_STATUS_DEV_BOUND) { + printf("dev "); + if (info->ifindex) { + char name[IF_NAMESIZE]; + + if (!if_indextoname(info->ifindex, name)) + printf("ifindex:%d ", info->ifindex); + else + printf("%s ", name); + } else { + printf("unknown "); + } + } printf("\n"); if (info->load_time) { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 7cebba491011..e92f62cf933a 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -259,6 +259,7 @@ union bpf_attr { __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; + __u32 prog_target_ifindex; /* ifindex of netdev to prep for */ }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -893,6 +894,10 @@ enum sk_action { #define BPF_TAG_SIZE 8 +enum bpf_prog_status { + BPF_PROG_STATUS_DEV_BOUND = (1 << 0), +}; + struct bpf_prog_info { __u32 type; __u32 id; @@ -906,6 +911,8 @@ struct bpf_prog_info { __u32 nr_map_ids; __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; + __u32 ifindex; + __u32 status; } __attribute__((aligned(8))); struct bpf_map_info { -- cgit v1.2.3 From 4990f1f4610b483a60397ed2768d268df228a551 Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Wed, 8 Nov 2017 13:55:48 +0900 Subject: tools: bpftool: show filenames of pinned objects Added support to show filenames of pinned objects. For example: root@test# ./bpftool prog 3: tracepoint name tracepoint__irq tag f677a7dd722299a3 loaded_at Oct 26/11:39 uid 0 xlated 160B not jited memlock 4096B map_ids 4 pinned /sys/fs/bpf/softirq_prog 4: tracepoint name tracepoint__irq tag ea5dc530d00b92b6 loaded_at Oct 26/11:39 uid 0 xlated 392B not jited memlock 4096B map_ids 4,6 root@test# ./bpftool --json --pretty prog [{ "id": 3, "type": "tracepoint", "name": "tracepoint__irq", "tag": "f677a7dd722299a3", "loaded_at": "Oct 26/11:39", "uid": 0, "bytes_xlated": 160, "jited": false, "bytes_memlock": 4096, "map_ids": [4 ], "pinned": ["/sys/fs/bpf/softirq_prog" ] },{ "id": 4, "type": "tracepoint", "name": "tracepoint__irq", "tag": "ea5dc530d00b92b6", "loaded_at": "Oct 26/11:39", "uid": 0, "bytes_xlated": 392, "jited": false, "bytes_memlock": 4096, "map_ids": [4,6 ], "pinned": [] } ] root@test# ./bpftool map 4: hash name start flags 0x0 key 4B value 16B max_entries 10240 memlock 1003520B pinned /sys/fs/bpf/softirq_map1 5: hash name iptr flags 0x0 key 4B value 8B max_entries 10240 memlock 921600B root@test# ./bpftool --json --pretty map [{ "id": 4, "type": "hash", "name": "start", "flags": 0, "bytes_key": 4, "bytes_value": 16, "max_entries": 10240, "bytes_memlock": 1003520, "pinned": ["/sys/fs/bpf/softirq_map1" ] },{ "id": 5, "type": "hash", "name": "iptr", "flags": 0, "bytes_key": 4, "bytes_value": 8, "max_entries": 10240, "bytes_memlock": 921600, "pinned": [] } ] Signed-off-by: Prashant Bhole Signed-off-by: David S. Miller --- tools/bpf/bpftool/common.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.c | 8 +++++ tools/bpf/bpftool/main.h | 17 ++++++++++ tools/bpf/bpftool/map.c | 21 ++++++++++++ tools/bpf/bpftool/prog.c | 24 ++++++++++++++ 5 files changed, 152 insertions(+) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c index 6b3d25d6a782..2bd3b280e6dd 100644 --- a/tools/bpf/bpftool/common.c +++ b/tools/bpf/bpftool/common.c @@ -34,7 +34,9 @@ /* Author: Jakub Kicinski */ #include +#include #include +#include #include #include #include @@ -321,3 +323,83 @@ void print_hex_data_json(uint8_t *data, size_t len) jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]); jsonw_end_array(json_wtr); } + +int build_pinned_obj_table(struct pinned_obj_table *tab, + enum bpf_obj_type type) +{ + struct bpf_prog_info pinned_info = {}; + struct pinned_obj *obj_node = NULL; + __u32 len = sizeof(pinned_info); + struct mntent *mntent = NULL; + enum bpf_obj_type objtype; + FILE *mntfile = NULL; + FTSENT *ftse = NULL; + FTS *fts = NULL; + int fd, err; + + mntfile = setmntent("/proc/mounts", "r"); + if (!mntfile) + return -1; + + while ((mntent = getmntent(mntfile))) { + char *path[] = { mntent->mnt_dir, NULL }; + + if (strncmp(mntent->mnt_type, "bpf", 3) != 0) + continue; + + fts = fts_open(path, 0, NULL); + if (!fts) + continue; + + while ((ftse = fts_read(fts))) { + if (!(ftse->fts_info & FTS_F)) + continue; + fd = open_obj_pinned(ftse->fts_path); + if (fd < 0) + continue; + + objtype = get_fd_type(fd); + if (objtype != type) { + close(fd); + continue; + } + memset(&pinned_info, 0, sizeof(pinned_info)); + err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len); + if (err) { + close(fd); + continue; + } + + obj_node = malloc(sizeof(*obj_node)); + if (!obj_node) { + close(fd); + fts_close(fts); + fclose(mntfile); + return -1; + } + + memset(obj_node, 0, sizeof(*obj_node)); + obj_node->id = pinned_info.id; + obj_node->path = strdup(ftse->fts_path); + hash_add(tab->table, &obj_node->hash, obj_node->id); + + close(fd); + } + fts_close(fts); + } + fclose(mntfile); + return 0; +} + +void delete_pinned_obj_table(struct pinned_obj_table *tab) +{ + struct pinned_obj *obj; + struct hlist_node *tmp; + unsigned int bkt; + + hash_for_each_safe(tab->table, bkt, tmp, obj, hash) { + hash_del(&obj->hash); + free(obj->path); + free(obj); + } +} diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 78d9afb74ef4..6ad53f1797fa 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -54,6 +54,8 @@ static int (*last_do_help)(int argc, char **argv); json_writer_t *json_wtr; bool pretty_output; bool json_output; +struct pinned_obj_table prog_table; +struct pinned_obj_table map_table; void usage(void) { @@ -272,6 +274,9 @@ int main(int argc, char **argv) json_output = false; bin_name = argv[0]; + hash_init(prog_table.table); + hash_init(map_table.table); + while ((opt = getopt_long(argc, argv, "Vhpj", options, NULL)) >= 0) { switch (opt) { @@ -311,5 +316,8 @@ int main(int argc, char **argv) if (json_output) jsonw_destroy(&json_wtr); + delete_pinned_obj_table(&prog_table); + delete_pinned_obj_table(&map_table); + return ret; } diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 2ff2a361af0d..13453de2e570 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "json_writer.h" @@ -70,6 +71,8 @@ extern const char *bin_name; extern json_writer_t *json_wtr; extern bool json_output; +extern struct pinned_obj_table prog_table; +extern struct pinned_obj_table map_table; void p_err(const char *fmt, ...); void p_info(const char *fmt, ...); @@ -78,6 +81,20 @@ bool is_prefix(const char *pfx, const char *str); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void usage(void) __attribute__((noreturn)); +struct pinned_obj_table { + DECLARE_HASHTABLE(table, 16); +}; + +struct pinned_obj { + __u32 id; + char *path; + struct hlist_node hash; +}; + +int build_pinned_obj_table(struct pinned_obj_table *table, + enum bpf_obj_type type); +void delete_pinned_obj_table(struct pinned_obj_table *tab); + struct cmd { const char *cmd; int (*func)(int argc, char **argv); diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index e978ab23a77f..de0980657cef 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -436,6 +436,18 @@ static int show_map_close_json(int fd, struct bpf_map_info *info) jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); free(memlock); + if (!hash_empty(map_table.table)) { + struct pinned_obj *obj; + + jsonw_name(json_wtr, "pinned"); + jsonw_start_array(json_wtr); + hash_for_each_possible(map_table.table, obj, hash, info->id) { + if (obj->id == info->id) + jsonw_string(json_wtr, obj->path); + } + jsonw_end_array(json_wtr); + } + jsonw_end_object(json_wtr); return 0; @@ -466,7 +478,14 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info) free(memlock); printf("\n"); + if (!hash_empty(map_table.table)) { + struct pinned_obj *obj; + hash_for_each_possible(map_table.table, obj, hash, info->id) { + if (obj->id == info->id) + printf("\tpinned %s\n", obj->path); + } + } return 0; } @@ -478,6 +497,8 @@ static int do_show(int argc, char **argv) int err; int fd; + build_pinned_obj_table(&map_table, BPF_OBJ_MAP); + if (argc == 2) { fd = map_parse_fd_and_info(&argc, &argv, &info, &len); if (fd < 0) diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index d3ab808dc882..8f94b8ac2e63 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -272,6 +272,18 @@ static void print_prog_json(struct bpf_prog_info *info, int fd) if (info->nr_map_ids) show_prog_maps(fd, info->nr_map_ids); + if (!hash_empty(prog_table.table)) { + struct pinned_obj *obj; + + jsonw_name(json_wtr, "pinned"); + jsonw_start_array(json_wtr); + hash_for_each_possible(prog_table.table, obj, hash, info->id) { + if (obj->id == info->id) + jsonw_string(json_wtr, obj->path); + } + jsonw_end_array(json_wtr); + } + jsonw_end_object(json_wtr); } @@ -331,6 +343,16 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd) if (info->nr_map_ids) show_prog_maps(fd, info->nr_map_ids); + if (!hash_empty(prog_table.table)) { + struct pinned_obj *obj; + + printf("\n"); + hash_for_each_possible(prog_table.table, obj, hash, info->id) { + if (obj->id == info->id) + printf("\tpinned %s\n", obj->path); + } + } + printf("\n"); } @@ -360,6 +382,8 @@ static int do_show(int argc, char **argv) int err; int fd; + build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); + if (argc == 2) { fd = prog_parse_fd(&argc, &argv); if (fd < 0) -- cgit v1.2.3 From c541b73466549c4aa4ee20ccd04ba52e4c95d6eb Mon Sep 17 00:00:00 2001 From: Prashant Bhole Date: Wed, 8 Nov 2017 13:55:49 +0900 Subject: tools: bpftool: optionally show filenames of pinned objects Making it optional to show file names of pinned objects because it scans complete bpf-fs filesystem which is costly. Added option -f|--bpffs. Documentation updated. Signed-off-by: Prashant Bhole Signed-off-by: David S. Miller --- tools/bpf/bpftool/Documentation/bpftool-map.rst | 5 ++++- tools/bpf/bpftool/Documentation/bpftool-prog.rst | 5 ++++- tools/bpf/bpftool/main.c | 14 +++++++++++--- tools/bpf/bpftool/main.h | 3 ++- tools/bpf/bpftool/map.c | 3 ++- tools/bpf/bpftool/prog.c | 3 ++- 6 files changed, 25 insertions(+), 8 deletions(-) (limited to 'tools/bpf/bpftool/prog.c') diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst b/tools/bpf/bpftool/Documentation/bpftool-map.rst index abb9ee940b15..9f51a268eb06 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-map.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst @@ -12,7 +12,7 @@ SYNOPSIS **bpftool** [*OPTIONS*] **map** *COMMAND* - *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } *COMMANDS* := { **show** | **dump** | **update** | **lookup** | **getnext** | **delete** @@ -86,6 +86,9 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -f, --bpffs + Show file names of pinned maps. + EXAMPLES ======== **# bpftool map show** diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst index 0f25d3c39e05..36e8d1c3c40d 100644 --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst @@ -12,7 +12,7 @@ SYNOPSIS **bpftool** [*OPTIONS*] **prog** *COMMAND* - *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] } + *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } } *COMMANDS* := { **show** | **dump xlated** | **dump jited** | **pin** | **help** } @@ -75,6 +75,9 @@ OPTIONS -p, --pretty Generate human-readable JSON output. Implies **-j**. + -f, --bpffs + Show file names of pinned programs. + EXAMPLES ======== **# bpftool prog show** diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c index 6ad53f1797fa..d6e4762170a4 100644 --- a/tools/bpf/bpftool/main.c +++ b/tools/bpf/bpftool/main.c @@ -54,6 +54,7 @@ static int (*last_do_help)(int argc, char **argv); json_writer_t *json_wtr; bool pretty_output; bool json_output; +bool show_pinned; struct pinned_obj_table prog_table; struct pinned_obj_table map_table; @@ -265,6 +266,7 @@ int main(int argc, char **argv) { "help", no_argument, NULL, 'h' }, { "pretty", no_argument, NULL, 'p' }, { "version", no_argument, NULL, 'V' }, + { "bpffs", no_argument, NULL, 'f' }, { 0 } }; int opt, ret; @@ -272,12 +274,13 @@ int main(int argc, char **argv) last_do_help = do_help; pretty_output = false; json_output = false; + show_pinned = false; bin_name = argv[0]; hash_init(prog_table.table); hash_init(map_table.table); - while ((opt = getopt_long(argc, argv, "Vhpj", + while ((opt = getopt_long(argc, argv, "Vhpjf", options, NULL)) >= 0) { switch (opt) { case 'V': @@ -290,6 +293,9 @@ int main(int argc, char **argv) case 'j': json_output = true; break; + case 'f': + show_pinned = true; + break; default: usage(); } @@ -316,8 +322,10 @@ int main(int argc, char **argv) if (json_output) jsonw_destroy(&json_wtr); - delete_pinned_obj_table(&prog_table); - delete_pinned_obj_table(&map_table); + if (show_pinned) { + delete_pinned_obj_table(&prog_table); + delete_pinned_obj_table(&map_table); + } return ret; } diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h index 13453de2e570..9c191e222d6f 100644 --- a/tools/bpf/bpftool/main.h +++ b/tools/bpf/bpftool/main.h @@ -59,7 +59,7 @@ #define HELP_SPEC_PROGRAM \ "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" #define HELP_SPEC_OPTIONS \ - "OPTIONS := { {-j|--json} [{-p|--pretty}] }" + "OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }" enum bpf_obj_type { BPF_OBJ_UNKNOWN, @@ -71,6 +71,7 @@ extern const char *bin_name; extern json_writer_t *json_wtr; extern bool json_output; +extern bool show_pinned; extern struct pinned_obj_table prog_table; extern struct pinned_obj_table map_table; diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c index de0980657cef..e2450c8e88e6 100644 --- a/tools/bpf/bpftool/map.c +++ b/tools/bpf/bpftool/map.c @@ -497,7 +497,8 @@ static int do_show(int argc, char **argv) int err; int fd; - build_pinned_obj_table(&map_table, BPF_OBJ_MAP); + if (show_pinned) + build_pinned_obj_table(&map_table, BPF_OBJ_MAP); if (argc == 2) { fd = map_parse_fd_and_info(&argc, &argv, &info, &len); diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 8f94b8ac2e63..f45c44ef9bec 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c @@ -382,7 +382,8 @@ static int do_show(int argc, char **argv) int err; int fd; - build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); + if (show_pinned) + build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); if (argc == 2) { fd = prog_parse_fd(&argc, &argv); -- cgit v1.2.3