selftests/bpf: add test program for loading BPF ELF files
authorJesper Dangaard Brouer <brouer@redhat.com>
Thu, 8 Feb 2018 11:48:22 +0000 (12:48 +0100)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 8 Feb 2018 23:24:38 +0000 (00:24 +0100)
V2: Moved program into selftests/bpf from tools/libbpf

This program can be used on its own for testing/debugging if a
BPF ELF-object file can be loaded with libbpf (from tools/lib/bpf).

If something is wrong with the ELF object, the program have
a --debug mode that will display the ELF sections and especially
the skipped sections.  This allows for quickly identifying the
problematic ELF section number, which can be corrolated with the
readelf tool.

The program signal error via return codes, and also have
a --quiet mode, which is practical for use in scripts like
selftests/bpf.

Signed-off-by: Jesper Dangaard Brouer <brouer@redhat.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/test_libbpf_open.c [new file with mode: 0644]

index 566d6adc172a6a794a81d337bd6d15e4f9df41f5..3f48da0866ba2d3393d288f61e2a38fd2333a8a3 100644 (file)
@@ -20,7 +20,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test
        test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
        sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
        test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
-       sample_map_ret0.o test_tcpbpf_kern.o
+       sample_map_ret0.o test_tcpbpf_kern.o test_libbpf_open
 
 TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \
        test_offload.py
diff --git a/tools/testing/selftests/bpf/test_libbpf_open.c b/tools/testing/selftests/bpf/test_libbpf_open.c
new file mode 100644 (file)
index 0000000..8fcd1c0
--- /dev/null
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0
+ * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
+ */
+static const char *__doc__ =
+       "Libbpf test program for loading BPF ELF object files";
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <bpf/libbpf.h>
+#include <getopt.h>
+
+static const struct option long_options[] = {
+       {"help",        no_argument,            NULL, 'h' },
+       {"debug",       no_argument,            NULL, 'D' },
+       {"quiet",       no_argument,            NULL, 'q' },
+       {0, 0, NULL,  0 }
+};
+
+static void usage(char *argv[])
+{
+       int i;
+
+       printf("\nDOCUMENTATION:\n%s\n\n", __doc__);
+       printf(" Usage: %s (options-see-below) BPF_FILE\n", argv[0]);
+       printf(" Listing options:\n");
+       for (i = 0; long_options[i].name != 0; i++) {
+               printf(" --%-12s", long_options[i].name);
+               printf(" short-option: -%c",
+                      long_options[i].val);
+               printf("\n");
+       }
+       printf("\n");
+}
+
+#define DEFINE_PRINT_FN(name, enabled) \
+static int libbpf_##name(const char *fmt, ...)         \
+{                                                      \
+        va_list args;                                  \
+        int ret;                                       \
+                                                       \
+        va_start(args, fmt);                           \
+       if (enabled) {                                  \
+               fprintf(stderr, "[" #name "] ");        \
+               ret = vfprintf(stderr, fmt, args);      \
+       }                                               \
+        va_end(args);                                  \
+        return ret;                                    \
+}
+DEFINE_PRINT_FN(warning, 1)
+DEFINE_PRINT_FN(info, 1)
+DEFINE_PRINT_FN(debug, 1)
+
+#define EXIT_FAIL_LIBBPF EXIT_FAILURE
+#define EXIT_FAIL_OPTION 2
+
+int test_walk_progs(struct bpf_object *obj, bool verbose)
+{
+       struct bpf_program *prog;
+       int cnt = 0;
+
+       bpf_object__for_each_program(prog, obj) {
+               cnt++;
+               if (verbose)
+                       printf("Prog (count:%d) section_name: %s\n", cnt,
+                              bpf_program__title(prog, false));
+       }
+       return 0;
+}
+
+int test_walk_maps(struct bpf_object *obj, bool verbose)
+{
+       struct bpf_map *map;
+       int cnt = 0;
+
+       bpf_map__for_each(map, obj) {
+               cnt++;
+               if (verbose)
+                       printf("Map (count:%d) name: %s\n", cnt,
+                              bpf_map__name(map));
+       }
+       return 0;
+}
+
+int test_open_file(char *filename, bool verbose)
+{
+       struct bpf_object *bpfobj = NULL;
+       long err;
+
+       if (verbose)
+               printf("Open BPF ELF-file with libbpf: %s\n", filename);
+
+       /* Load BPF ELF object file and check for errors */
+       bpfobj = bpf_object__open(filename);
+       err = libbpf_get_error(bpfobj);
+       if (err) {
+               char err_buf[128];
+               libbpf_strerror(err, err_buf, sizeof(err_buf));
+               if (verbose)
+                       printf("Unable to load eBPF objects in file '%s': %s\n",
+                              filename, err_buf);
+               return EXIT_FAIL_LIBBPF;
+       }
+       test_walk_progs(bpfobj, verbose);
+       test_walk_maps(bpfobj, verbose);
+
+       if (verbose)
+               printf("Close BPF ELF-file with libbpf: %s\n",
+                      bpf_object__name(bpfobj));
+       bpf_object__close(bpfobj);
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       char filename[1024] = { 0 };
+       bool verbose = 1;
+       int longindex = 0;
+       int opt;
+
+       libbpf_set_print(libbpf_warning, libbpf_info, NULL);
+
+       /* Parse commands line args */
+       while ((opt = getopt_long(argc, argv, "hDq",
+                                 long_options, &longindex)) != -1) {
+               switch (opt) {
+               case 'D':
+                       libbpf_set_print(libbpf_warning, libbpf_info,
+                                        libbpf_debug);
+                       break;
+               case 'q': /* Use in scripting mode */
+                       verbose = 0;
+                       break;
+               case 'h':
+               default:
+                       usage(argv);
+                       return EXIT_FAIL_OPTION;
+               }
+       }
+       if (optind >= argc) {
+               usage(argv);
+               printf("ERROR: Expected BPF_FILE argument after options\n");
+               return EXIT_FAIL_OPTION;
+       }
+       snprintf(filename, sizeof(filename), "%s", argv[optind]);
+
+       return test_open_file(filename, verbose);
+}