All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/3] BPF mass-verification veristat tool
@ 2022-08-26 23:15 Andrii Nakryiko
  2022-08-26 23:15 ` [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations Andrii Nakryiko
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Andrii Nakryiko @ 2022-08-26 23:15 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Add a simple mass-verification and stats collection tool, veristat, to
selftests/bpf for use by everyone. See patch #3 for some more details.

Patch #1 fixes two BPF objects to be more usable with veristat.

Patch #2 fixes libbpf bug which also was discovered trying to mass-verify
a bunch of internal Meta BPF object files.

Andrii Nakryiko (3):
  selftests/bpf: fix test_verif_scale{1,3} SEC() annotations
  libbpf: fix crash if SEC("freplace") programs don't have
    attach_prog_fd set
  selftests/bpf: add veristat tool for mass-verifying BPF object files

 tools/lib/bpf/libbpf.c                        |  13 +-
 tools/testing/selftests/bpf/.gitignore        |   1 +
 tools/testing/selftests/bpf/Makefile          |   6 +-
 .../selftests/bpf/progs/test_verif_scale1.c   |   2 +-
 .../selftests/bpf/progs/test_verif_scale3.c   |   2 +-
 tools/testing/selftests/bpf/veristat.c        | 541 ++++++++++++++++++
 6 files changed, 558 insertions(+), 7 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/veristat.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations
  2022-08-26 23:15 [PATCH bpf-next 0/3] BPF mass-verification veristat tool Andrii Nakryiko
@ 2022-08-26 23:15 ` Andrii Nakryiko
  2022-08-27 23:43   ` KP Singh
  2022-08-26 23:15 ` [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set Andrii Nakryiko
  2022-08-26 23:15 ` [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files Andrii Nakryiko
  2 siblings, 1 reply; 12+ messages in thread
From: Andrii Nakryiko @ 2022-08-26 23:15 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Use proper SEC("tc") for test_verif_scale{1,3} programs. It's not
a problem for selftests right now because we manually set type
programmatically, but not having correct SEC() definitions makes it
harded to generically load BPF object files.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/testing/selftests/bpf/progs/test_verif_scale1.c | 2 +-
 tools/testing/selftests/bpf/progs/test_verif_scale3.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/test_verif_scale1.c b/tools/testing/selftests/bpf/progs/test_verif_scale1.c
index d38153dab3dd..ac6135d9374c 100644
--- a/tools/testing/selftests/bpf/progs/test_verif_scale1.c
+++ b/tools/testing/selftests/bpf/progs/test_verif_scale1.c
@@ -5,7 +5,7 @@
 #define ATTR __attribute__((noinline))
 #include "test_jhash.h"
 
-SEC("scale90_noinline")
+SEC("tc")
 int balancer_ingress(struct __sk_buff *ctx)
 {
 	void *data_end = (void *)(long)ctx->data_end;
diff --git a/tools/testing/selftests/bpf/progs/test_verif_scale3.c b/tools/testing/selftests/bpf/progs/test_verif_scale3.c
index 9beb5bf80373..ca33a9b711c4 100644
--- a/tools/testing/selftests/bpf/progs/test_verif_scale3.c
+++ b/tools/testing/selftests/bpf/progs/test_verif_scale3.c
@@ -5,7 +5,7 @@
 #define ATTR __attribute__((noinline))
 #include "test_jhash.h"
 
-SEC("scale90_noinline32")
+SEC("tc")
 int balancer_ingress(struct __sk_buff *ctx)
 {
 	void *data_end = (void *)(long)ctx->data_end;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set
  2022-08-26 23:15 [PATCH bpf-next 0/3] BPF mass-verification veristat tool Andrii Nakryiko
  2022-08-26 23:15 ` [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations Andrii Nakryiko
@ 2022-08-26 23:15 ` Andrii Nakryiko
  2022-08-27 23:45   ` KP Singh
  2022-08-26 23:15 ` [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files Andrii Nakryiko
  2 siblings, 1 reply; 12+ messages in thread
From: Andrii Nakryiko @ 2022-08-26 23:15 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Fix SIGSEGV caused by libbpf trying to find attach type in vmlinux BTF
for freplace programs. It's wrong to search in vmlinux BTF and libbpf
doesn't even mark vmlinux BTF as required for freplace programs. So
trying to search anything in obj->vmlinux_btf might cause NULL
dereference if nothing else in BPF object requires vmlinux BTF.

Instead, error out if freplace (EXT) program doesn't specify
attach_prog_fd during at the load time.

Fixes: 91abb4a6d79d ("libbpf: Support attachment of BPF tracing programs to kernel modules")
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/lib/bpf/libbpf.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3ad139285fad..2ca30ccc774c 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -9084,11 +9084,15 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attac
 	int err = 0;
 
 	/* BPF program's BTF ID */
-	if (attach_prog_fd) {
+	if (prog->type == BPF_PROG_TYPE_EXT || attach_prog_fd) {
+		if (!attach_prog_fd) {
+			pr_warn("prog '%s': attach program FD is not set\n", prog->name);
+			return -EINVAL;
+		}
 		err = libbpf_find_prog_btf_id(attach_name, attach_prog_fd);
 		if (err < 0) {
-			pr_warn("failed to find BPF program (FD %d) BTF ID for '%s': %d\n",
-				 attach_prog_fd, attach_name, err);
+			pr_warn("prog '%s': failed to find BPF program (FD %d) BTF ID for '%s': %d\n",
+				 prog->name, attach_prog_fd, attach_name, err);
 			return err;
 		}
 		*btf_obj_fd = 0;
@@ -9105,7 +9109,8 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attac
 		err = find_kernel_btf_id(prog->obj, attach_name, attach_type, btf_obj_fd, btf_type_id);
 	}
 	if (err) {
-		pr_warn("failed to find kernel BTF type ID of '%s': %d\n", attach_name, err);
+		pr_warn("prog '%s': failed to find kernel BTF type ID of '%s': %d\n",
+			prog->name, attach_name, err);
 		return err;
 	}
 	return 0;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-26 23:15 [PATCH bpf-next 0/3] BPF mass-verification veristat tool Andrii Nakryiko
  2022-08-26 23:15 ` [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations Andrii Nakryiko
  2022-08-26 23:15 ` [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set Andrii Nakryiko
@ 2022-08-26 23:15 ` Andrii Nakryiko
  2022-08-27 23:53   ` KP Singh
  2022-08-29  6:02   ` Kui-Feng Lee
  2 siblings, 2 replies; 12+ messages in thread
From: Andrii Nakryiko @ 2022-08-26 23:15 UTC (permalink / raw)
  To: bpf, ast, daniel; +Cc: andrii, kernel-team

Add a small tool, veristat, that allows mass-verification of
a set of *libbpf-compatible* BPF ELF object files. For each such object
file, veristat will attempt to verify each BPF program *individually*.
Regardless of success or failure, it parses BPF verifier stats and
outputs them in human-readable table format. In the future we can also
add CSV and JSON output for more scriptable post-processing, if necessary.

veristat allows to specify a set of stats that should be output and
ordering between multiple objects and files (e.g., so that one can
easily order by total instructions processed, instead of default file
name, prog name, verdict, total instructions order).

This tool should be useful for validating various BPF verifier changes
or even validating different kernel versions for regressions.

Here's an example for some of the heaviest selftests/bpf BPF object
files:

  $ sudo ./veristat -s insns,file,prog {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*.linked3.o
  File                                  Program                               Verdict  Duration, us  Total insns  Total states  Peak states
  ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------
  loop3.linked3.o                       while_true                            failure        350990      1000001          9663         9663
  test_verif_scale3.linked3.o           balancer_ingress                      success        115244       845499          8636         2141
  test_verif_scale2.linked3.o           balancer_ingress                      success         77688       773445          3048          788
  pyperf600.linked3.o                   on_event                              success       2079872       624585         30335        30241
  pyperf600_nounroll.linked3.o          on_event                              success        353972       568128         37101         2115
  strobemeta.linked3.o                  on_event                              success        455230       557149         15915        13537
  test_verif_scale1.linked3.o           balancer_ingress                      success         89880       554754          8636         2141
  strobemeta_nounroll2.linked3.o        on_event                              success        433906       501725         17087         1912
  loop6.linked3.o                       trace_virtqueue_add_sgs               success        282205       398057          8717          919
  loop1.linked3.o                       nested_loops                          success        125630       361349          5504         5504
  pyperf180.linked3.o                   on_event                              success       2511740       160398         11470        11446
  pyperf100.linked3.o                   on_event                              success        744329        87681          6213         6191
  test_cls_redirect.linked3.o           cls_redirect                          success         54087        78925          4782          903
  strobemeta_subprogs.linked3.o         on_event                              success         57898        65420          1954          403
  test_cls_redirect_subprogs.linked3.o  cls_redirect                          success         54522        64965          4619          958
  strobemeta_nounroll1.linked3.o        on_event                              success         43313        57240          1757          382
  pyperf50.linked3.o                    on_event                              success        194355        46378          3263         3241
  profiler2.linked3.o                   tracepoint__syscalls__sys_enter_kill  success         23869        43372          1423          542
  pyperf_subprogs.linked3.o             on_event                              success         29179        36358          2499         2499
  profiler1.linked3.o                   tracepoint__syscalls__sys_enter_kill  success         13052        27036          1946          936
  profiler3.linked3.o                   tracepoint__syscalls__sys_enter_kill  success         21023        26016          2186          915
  profiler2.linked3.o                   kprobe__vfs_link                      success          5255        13896           303          271
  profiler1.linked3.o                   kprobe__vfs_link                      success          7792        12687          1042         1041
  profiler3.linked3.o                   kprobe__vfs_link                      success          7332        10601           865          865
  profiler2.linked3.o                   kprobe_ret__do_filp_open              success          3417         8900           216          199
  profiler2.linked3.o                   kprobe__vfs_symlink                   success          3548         8775           203          186
  pyperf_global.linked3.o               on_event                              success         10007         7563           520          520
  profiler3.linked3.o                   kprobe_ret__do_filp_open              success          4708         6464           532          532
  profiler1.linked3.o                   kprobe_ret__do_filp_open              success          3090         6445           508          508
  profiler3.linked3.o                   kprobe__vfs_symlink                   success          4477         6358           521          521
  profiler1.linked3.o                   kprobe__vfs_symlink                   success          3381         6347           507          507
  profiler2.linked3.o                   raw_tracepoint__sched_process_exec    success          2464         5874           292          189
  profiler3.linked3.o                   raw_tracepoint__sched_process_exec    success          2677         4363           397          283
  profiler2.linked3.o                   kprobe__proc_sys_write                success          1800         4355           143          138
  profiler1.linked3.o                   raw_tracepoint__sched_process_exec    success          1649         4019           333          240
  pyperf600_bpf_loop.linked3.o          on_event                              success          2711         3966           306          306
  profiler2.linked3.o                   raw_tracepoint__sched_process_exit    success          1234         3138            83           66
  profiler3.linked3.o                   kprobe__proc_sys_write                success          1755         2623           223          223
  profiler1.linked3.o                   kprobe__proc_sys_write                success          1222         2456           193          193
  loop2.linked3.o                       while_true                            success           608         1783            57           30
  profiler3.linked3.o                   raw_tracepoint__sched_process_exit    success           789         1680           146          146
  profiler1.linked3.o                   raw_tracepoint__sched_process_exit    success           592         1526           133          133
  strobemeta_bpf_loop.linked3.o         on_event                              success          1015         1512           106          106
  loop4.linked3.o                       combinations                          success           165          524            18           17
  profiler3.linked3.o                   raw_tracepoint__sched_process_fork    success           196          299            25           25
  profiler1.linked3.o                   raw_tracepoint__sched_process_fork    success           109          265            19           19
  profiler2.linked3.o                   raw_tracepoint__sched_process_fork    success           111          265            19           19
  loop5.linked3.o                       while_true                            success            47           84             9            9
  ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/testing/selftests/bpf/.gitignore |   1 +
 tools/testing/selftests/bpf/Makefile   |   6 +-
 tools/testing/selftests/bpf/veristat.c | 541 +++++++++++++++++++++++++
 3 files changed, 547 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/veristat.c

diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 3a8cb2404ea6..3b288562963e 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -39,6 +39,7 @@ test_cpp
 /tools
 /runqslower
 /bench
+/veristat
 *.ko
 *.tmp
 xskxceiver
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index eecad99f1735..7a81dda8215f 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -82,7 +82,7 @@ TEST_PROGS_EXTENDED := with_addr.sh \
 TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \
 	flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
 	test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
-	xskxceiver xdp_redirect_multi xdp_synproxy
+	xskxceiver xdp_redirect_multi xdp_synproxy veristat
 
 TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
 
@@ -594,6 +594,10 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
 	$(call msg,BINARY,,$@)
 	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
 
+$(OUTPUT)/veristat: $(OUTPUT)/veristat.o $(BPFOBJ)
+	$(call msg,BINARY,,$@)
+	$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
+
 EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR)	\
 	prog_tests/tests.h map_tests/tests.h verifier/tests.h		\
 	feature bpftool							\
diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
new file mode 100644
index 000000000000..133cf3bb8eda
--- /dev/null
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
+#define _GNU_SOURCE
+#include <argp.h>
+#include <string.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+#include <sched.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/sysinfo.h>
+#include <sys/stat.h>
+#include <bpf/libbpf.h>
+
+enum stat_id {
+	VERDICT,
+	DURATION,
+	TOTAL_INSNS,
+	TOTAL_STATES,
+	PEAK_STATES,
+	MAX_STATES_PER_INSN,
+	MARK_READ_MAX_LEN,
+
+	FILE_NAME,
+	PROG_NAME,
+
+	ALL_STATS_CNT,
+	NUM_STATS_CNT = FILE_NAME - VERDICT,
+};
+
+struct verif_stats {
+	char *file_name;
+	char *prog_name;
+	
+	long stats[NUM_STATS_CNT];
+};
+
+struct stat_specs {
+	int spec_cnt;
+	enum stat_id ids[ALL_STATS_CNT];
+	bool asc[ALL_STATS_CNT];
+	int lens[ALL_STATS_CNT];
+};
+
+static struct env {
+	char **filenames;
+	int filename_cnt;
+	bool verbose;
+
+	struct verif_stats *prog_stats;
+	int prog_stat_cnt;
+
+	struct stat_specs output_spec;
+	struct stat_specs sort_spec;
+} env;
+
+static int libbpf_print_fn(enum libbpf_print_level level,
+		    const char *format, va_list args)
+{
+	if (!env.verbose)
+		return 0;
+	if (level == LIBBPF_DEBUG /* && !env.verbose */)
+		return 0;
+	return vfprintf(stderr, format, args);
+}
+
+const char *argp_program_version = "veristat";
+const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
+const char argp_program_doc[] =
+"veristat    BPF verifier stats collection tool.\n"
+"\n"
+"USAGE: veristat <obj-file> [<obj-file>...]\n";
+
+static const struct argp_option opts[] = {
+	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
+	{ "verbose", 'v', NULL, 0, "Verbose mode" },
+	{ "output", 'o', "SPEC", 0, "Specify output stats" },
+	{ "sort", 's', "SPEC", 0, "Specify sort order" },
+	{},
+};
+
+static int parse_stats(const char *stats_str, struct stat_specs *specs);
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+	void *tmp;
+	int err;
+
+	switch (key) {
+	case 'h':
+		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
+		break;
+	case 'v':
+		env.verbose = true;
+		break;
+	case 'o':
+		err = parse_stats(arg, &env.output_spec);
+		if (err)
+			return err;
+		break;
+	case 's':
+		err = parse_stats(arg, &env.sort_spec);
+		if (err)
+			return err;
+		break;
+	case ARGP_KEY_ARG:
+		tmp = realloc(env.filenames, (env.filename_cnt + 1) * sizeof(*env.filenames));
+		if (!tmp)
+			return -ENOMEM;
+		env.filenames = tmp;
+		env.filenames[env.filename_cnt] = strdup(arg);
+		if (!env.filenames[env.filename_cnt])
+			return -ENOMEM;
+		env.filename_cnt++;
+		break;
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static const struct stat_specs default_output_spec = {
+	.spec_cnt = 7,
+	.ids = {
+		FILE_NAME, PROG_NAME, VERDICT, DURATION,
+		TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
+	},
+};
+
+static const struct stat_specs default_sort_spec = {
+	.spec_cnt = 2,
+	.ids = {
+		FILE_NAME, PROG_NAME,
+	},
+	.asc = { true, true, },
+};
+
+static struct stat_def {
+	const char *header;
+	const char *names[4];
+	bool asc_by_default;
+} stat_defs[] = {
+	[FILE_NAME] = { "File", {"file_name", "filename", "file"}, true /* asc */ },
+	[PROG_NAME] = { "Program", {"prog_name", "progname", "prog"}, true /* asc */ },
+	[VERDICT] = { "Verdict", {"verdict"}, true /* asc: failure, success */ },
+	[DURATION] = { "Duration, us", {"duration", "dur"}, },
+	[TOTAL_INSNS] = { "Total insns", {"total_insns", "insns"}, },
+	[TOTAL_STATES] = { "Total states", {"total_states", "states"}, },
+	[PEAK_STATES] = { "Peak states", {"peak_states"}, },
+	[MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
+	[MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
+};
+
+static int parse_stat(const char *stat_name, struct stat_specs *specs)
+{
+	int id, i;
+
+	if (specs->spec_cnt >= ARRAY_SIZE(specs->ids)) {
+		fprintf(stderr, "Can't specify more than %zd stats\n", ARRAY_SIZE(specs->ids));
+		return -E2BIG;
+	}
+
+	for (id = 0; id < ARRAY_SIZE(stat_defs); id++) {
+		struct stat_def *def = &stat_defs[id];
+
+		for (i = 0; i < ARRAY_SIZE(stat_defs[id].names); i++) {
+			if (!def->names[i] || strcmp(def->names[i], stat_name) != 0)
+				continue;
+
+			specs->ids[specs->spec_cnt] = id;
+			specs->asc[specs->spec_cnt] = def->asc_by_default;
+			specs->spec_cnt++;
+
+			return 0;
+		}
+	}
+
+	fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name);
+	return -ESRCH;
+}
+
+static int parse_stats(const char *stats_str, struct stat_specs *specs)
+{
+	char *input, *state = NULL, *next;
+	int err;
+
+	input = strdup(stats_str);
+	if (!input)
+		return -ENOMEM;
+
+	while ((next = strtok_r(state ? NULL : input, ",", &state))) {
+		err = parse_stat(next, specs);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static char verif_log_buf[64 * 1024];
+
+static int parse_verif_log(const char *buf, size_t buf_sz, struct verif_stats *s)
+{
+	const char *next;
+	int pos;
+
+	for (pos = 0; buf[0]; buf = next) {
+		if (buf[0] == '\n')
+			buf++;
+		next = strchrnul(&buf[pos], '\n');
+
+		if (1 == sscanf(buf, "verification time %ld usec\n", &s->stats[DURATION]))
+			continue;
+		if (6 == sscanf(buf, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
+				&s->stats[TOTAL_INSNS],
+				&s->stats[MAX_STATES_PER_INSN],
+				&s->stats[TOTAL_STATES],
+				&s->stats[PEAK_STATES],
+				&s->stats[MARK_READ_MAX_LEN]))
+			continue;
+	}
+
+	return 0;
+}
+
+static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
+{
+	const char *prog_name = bpf_program__name(prog);
+	size_t buf_sz = sizeof(verif_log_buf);
+	char *buf = verif_log_buf;
+	struct verif_stats *stats;
+	int err = 0;
+	void *tmp;
+
+	tmp = realloc(env.prog_stats, (env.prog_stat_cnt + 1) * sizeof(*env.prog_stats));
+	if (!tmp)
+		return -ENOMEM;
+	env.prog_stats = tmp;
+	stats = &env.prog_stats[env.prog_stat_cnt++];
+	memset(stats, 0, sizeof(*stats));
+
+	if (env.verbose) {
+		buf_sz = 16 * 1024 * 1024;
+		buf = malloc(buf_sz);
+		if (!buf)
+			return -ENOMEM;
+		bpf_program__set_log_buf(prog, buf, buf_sz);
+		bpf_program__set_log_level(prog, 1 | 4); /* stats + log */
+	} else {
+		bpf_program__set_log_buf(prog, buf, buf_sz);
+		bpf_program__set_log_level(prog, 4); /* only verifier stats */
+	}
+	verif_log_buf[0] = '\0';
+
+	err = bpf_object__load(obj);
+
+	stats->file_name = strdup(basename(filename));
+	stats->prog_name = strdup(bpf_program__name(prog));
+	stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
+	parse_verif_log(buf, buf_sz, stats);
+
+	if (env.verbose) {
+		printf("PROCESSING %s/%s, DURATION US: %ld, VERDICT: %s, VERIFIER LOG:\n%s\n",
+		       filename, prog_name, stats->stats[DURATION],
+		       err ? "failure" : "success", buf);
+	}
+
+	if (verif_log_buf != buf)
+		free(buf);
+
+	return 0;
+};
+
+static int process_obj(const char *filename)
+{
+	struct bpf_object *obj = NULL, *tobj;
+	struct bpf_program *prog, *tprog, *lprog;
+	libbpf_print_fn_t old_libbpf_print_fn;
+	LIBBPF_OPTS(bpf_object_open_opts, opts);
+	int err = 0, prog_cnt = 0;
+
+	old_libbpf_print_fn = libbpf_set_print(libbpf_print_fn);
+
+	obj = bpf_object__open_file(filename, &opts);
+	if (!obj) {
+		err = -errno;
+		fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
+		goto cleanup;
+	}
+
+	bpf_object__for_each_program(prog, obj) {
+		prog_cnt++;
+	}
+
+	if (prog_cnt == 1) {
+		prog = bpf_object__next_program(obj, NULL);
+		bpf_program__set_autoload(prog, true);
+		process_prog(filename, obj, prog);
+		bpf_object__close(obj);
+		goto cleanup;
+	}
+
+	bpf_object__for_each_program(prog, obj) {
+		const char *prog_name = bpf_program__name(prog);
+
+		tobj = bpf_object__open_file(filename, &opts);
+		if (!tobj) {
+			err = -errno;
+			fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
+			goto cleanup;
+		}
+
+		bpf_object__for_each_program(tprog, tobj) {
+			const char *tprog_name = bpf_program__name(tprog);
+
+			if (strcmp(prog_name, tprog_name) == 0) {
+				bpf_program__set_autoload(tprog, true);
+				lprog = tprog;
+			} else {
+				bpf_program__set_autoload(tprog, false);
+			}
+		}
+
+		process_prog(filename, tobj, lprog);
+		bpf_object__close(tobj);
+	}
+
+cleanup:
+	libbpf_set_print(old_libbpf_print_fn);
+	return err;
+}
+
+static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
+		    enum stat_id id, bool asc)
+{
+	int cmp = 0;
+
+	switch (id) {
+	case FILE_NAME:
+		cmp = strcmp(s1->file_name, s2->file_name);
+		break;
+	case PROG_NAME:
+		cmp = strcmp(s1->prog_name, s2->prog_name);
+		break;
+	case VERDICT:
+	case DURATION:
+	case TOTAL_INSNS:
+	case TOTAL_STATES:
+	case PEAK_STATES:
+	case MAX_STATES_PER_INSN:
+	case MARK_READ_MAX_LEN: {
+		long v1 = s1->stats[id];
+		long v2 = s2->stats[id];
+
+		if (v1 != v2)
+			cmp = v1 < v2 ? -1 : 1;
+		break;
+	}
+	default:
+		fprintf(stderr, "Unrecognized stat #%d\n", id);
+		exit(1);
+	}
+
+	return asc ? cmp : -cmp;
+}
+
+static int cmp_prog_stats(const void *v1, const void *v2)
+{
+	const struct verif_stats *s1 = v1, *s2 = v2;
+	int i, cmp;
+
+	for (i = 0; i < env.sort_spec.spec_cnt; i++) {
+		cmp = cmp_stat(s1, s2, env.sort_spec.ids[i], env.sort_spec.asc[i]);
+		if (cmp != 0)
+			return cmp;
+	}
+
+	return 0;
+}
+
+#define snappendf(dst, len, fmt, args...)					\
+	len += snprintf(dst + len,						\
+			      sizeof(dst) < len ? 0 : sizeof(dst) - len,	\
+			      fmt, ##args)
+
+#define HEADER_CHAR '-'
+#define COLUMN_SEP "  "
+
+static void output_headers(bool calc_len)
+{
+	int i, len;
+
+	for (i = 0; i < env.output_spec.spec_cnt; i++) {
+		int id = env.output_spec.ids[i];
+		int *max_len = &env.output_spec.lens[i];
+
+		if (calc_len) {
+			len = snprintf(NULL, 0, "%s", stat_defs[id].header);
+			if (len > *max_len)
+				*max_len = len;
+		} else {
+			printf("%s%-*s", i == 0 ? "" : COLUMN_SEP,  *max_len, stat_defs[id].header);
+		}
+	}
+
+	if (!calc_len)
+		printf("\n");
+}
+
+static void output_lines(void)
+{
+	int i, j, len;
+
+	for (i = 0; i < env.output_spec.spec_cnt; i++) {
+		len = env.output_spec.lens[i];
+
+		printf("%s", i == 0 ? "" : COLUMN_SEP);
+		for (j = 0; j < len; j++)
+			printf("%c", HEADER_CHAR);
+	}
+	printf("\n");
+}
+
+static void output_stats(const struct verif_stats *s, bool calc_len)
+{
+	int i;
+
+	for (i = 0; i < env.output_spec.spec_cnt; i++) {
+		int id = env.output_spec.ids[i];
+		int *max_len = &env.output_spec.lens[i], len;
+		const char *str = NULL;
+		long val = 0;
+
+		switch (id) {
+		case FILE_NAME:
+			str = s->file_name;
+			break;
+		case PROG_NAME:
+			str = s->prog_name;
+			break;
+		case VERDICT:
+			str = s->stats[VERDICT] ? "success" : "failure";
+			break;
+		case DURATION:
+		case TOTAL_INSNS:
+		case TOTAL_STATES:
+		case PEAK_STATES:
+		case MAX_STATES_PER_INSN:
+		case MARK_READ_MAX_LEN:
+			val = s->stats[id];
+			break;
+		default:
+			fprintf(stderr, "Unrecognized stat #%d\n", id);
+			exit(1);
+		}
+		
+		if (calc_len) {
+			if (str)
+				len = snprintf(NULL, 0, "%s", str);
+			else
+				len = snprintf(NULL, 0, "%ld", val);
+			if (len > *max_len)
+				*max_len = len;
+		} else {
+			if (str)
+				printf("%s%-*s", i == 0 ? "" : COLUMN_SEP, *max_len, str);
+			else
+				printf("%s%*ld", i == 0 ? "" : COLUMN_SEP,  *max_len, val);
+		}
+	}
+
+	if (!calc_len)
+		printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+	static const struct argp argp = {
+		.options = opts,
+		.parser = parse_arg,
+		.doc = argp_program_doc,
+	};
+	int err = 0, i;
+
+	if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
+		return 1;
+
+	if (env.filename_cnt == 0) {
+		fprintf(stderr, "Please provide path to BPF object file!\n");
+		argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat");
+		return 1;
+	}
+
+	if (env.output_spec.spec_cnt == 0)
+		env.output_spec = default_output_spec;
+	if (env.sort_spec.spec_cnt == 0)
+		env.sort_spec = default_sort_spec;
+
+	for (i = 0; i < env.filename_cnt; i++) {
+		err = process_obj(env.filenames[i]);
+		if (err) {
+			fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err);
+			goto cleanup;
+		}
+	}
+
+	qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats);
+
+	/* calculate column widths */
+	output_headers(true);
+	for (i = 0; i < env.prog_stat_cnt; i++) {
+		output_stats(&env.prog_stats[i], true);
+	}
+
+	/* actually output the table */
+	output_headers(false);
+	output_lines();
+	for (i = 0; i < env.prog_stat_cnt; i++) {
+		output_stats(&env.prog_stats[i], false);
+	}
+	output_lines();
+	printf("\n");
+
+	printf("Done. Processed %d object files, %d programs.\n",
+	       env.filename_cnt, env.prog_stat_cnt);
+
+cleanup:
+	for (i = 0; i < env.prog_stat_cnt; i++) {
+		free(env.prog_stats[i].file_name);
+		free(env.prog_stats[i].prog_name);
+	}
+	free(env.prog_stats);
+	for (i = 0; i < env.filename_cnt; i++)
+		free(env.filenames[i]);
+	free(env.filenames);
+	return -err;
+}
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations
  2022-08-26 23:15 ` [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations Andrii Nakryiko
@ 2022-08-27 23:43   ` KP Singh
  0 siblings, 0 replies; 12+ messages in thread
From: KP Singh @ 2022-08-27 23:43 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, ast, daniel, kernel-team

On Sat, Aug 27, 2022 at 1:15 AM Andrii Nakryiko <andrii@kernel.org> wrote:
>
> Use proper SEC("tc") for test_verif_scale{1,3} programs. It's not
> a problem for selftests right now because we manually set type
> programmatically, but not having correct SEC() definitions makes it
> harded to generically load BPF object files.
>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

Acked-by: KP Singh <kpsingh@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set
  2022-08-26 23:15 ` [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set Andrii Nakryiko
@ 2022-08-27 23:45   ` KP Singh
  0 siblings, 0 replies; 12+ messages in thread
From: KP Singh @ 2022-08-27 23:45 UTC (permalink / raw)
  To: Andrii Nakryiko; +Cc: bpf, ast, daniel, kernel-team

On Sat, Aug 27, 2022 at 1:16 AM Andrii Nakryiko <andrii@kernel.org> wrote:
>
> Fix SIGSEGV caused by libbpf trying to find attach type in vmlinux BTF
> for freplace programs. It's wrong to search in vmlinux BTF and libbpf
> doesn't even mark vmlinux BTF as required for freplace programs. So
> trying to search anything in obj->vmlinux_btf might cause NULL
> dereference if nothing else in BPF object requires vmlinux BTF.
>
> Instead, error out if freplace (EXT) program doesn't specify
> attach_prog_fd during at the load time.
>
> Fixes: 91abb4a6d79d ("libbpf: Support attachment of BPF tracing programs to kernel modules")
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

Acked-by: KP Singh <kpsingh@kernel.org>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-26 23:15 ` [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files Andrii Nakryiko
@ 2022-08-27 23:53   ` KP Singh
  2022-08-29 14:50     ` Daniel Borkmann
  2022-09-09 19:04     ` Andrii Nakryiko
  2022-08-29  6:02   ` Kui-Feng Lee
  1 sibling, 2 replies; 12+ messages in thread
From: KP Singh @ 2022-08-27 23:53 UTC (permalink / raw)
  To: Andrii Nakryiko, Quentin Monnet; +Cc: bpf, ast, daniel, kernel-team

On Sat, Aug 27, 2022 at 1:15 AM Andrii Nakryiko <andrii@kernel.org> wrote:
>
> Add a small tool, veristat, that allows mass-verification of
> a set of *libbpf-compatible* BPF ELF object files. For each such object
> file, veristat will attempt to verify each BPF program *individually*.
> Regardless of success or failure, it parses BPF verifier stats and
> outputs them in human-readable table format. In the future we can also
> add CSV and JSON output for more scriptable post-processing, if necessary.
>
> veristat allows to specify a set of stats that should be output and
> ordering between multiple objects and files (e.g., so that one can
> easily order by total instructions processed, instead of default file
> name, prog name, verdict, total instructions order).
>
> This tool should be useful for validating various BPF verifier changes
> or even validating different kernel versions for regressions.

Cool stuff!

I think this would be useful for cases beyond these (i.e. for users to get
stats about the verifier in general) and it's worth thinking if this should
be built into bpftool?

>
> Here's an example for some of the heaviest selftests/bpf BPF object
> files:
>
>   $ sudo ./veristat -s insns,file,prog {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*.linked3.o
>   File                                  Program                               Verdict  Duration, us  Total insns  Total states  Peak states
>   ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------
>   loop3.linked3.o                       while_true                            failure        350990      1000001          9663         9663

[...]

> --
> 2.30.2
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-26 23:15 ` [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files Andrii Nakryiko
  2022-08-27 23:53   ` KP Singh
@ 2022-08-29  6:02   ` Kui-Feng Lee
  2022-09-09 19:18     ` Andrii Nakryiko
  1 sibling, 1 reply; 12+ messages in thread
From: Kui-Feng Lee @ 2022-08-29  6:02 UTC (permalink / raw)
  To: daniel, ast, andrii, bpf; +Cc: Kernel Team

On Fri, 2022-08-26 at 16:15 -0700, Andrii Nakryiko wrote:
> Add a small tool, veristat, that allows mass-verification of
> a set of *libbpf-compatible* BPF ELF object files. For each such
> object
> file, veristat will attempt to verify each BPF program
> *individually*.
> Regardless of success or failure, it parses BPF verifier stats and
> outputs them in human-readable table format. In the future we can
> also
> add CSV and JSON output for more scriptable post-processing, if
> necessary.
> 
> veristat allows to specify a set of stats that should be output and
> ordering between multiple objects and files (e.g., so that one can
> easily order by total instructions processed, instead of default file
> name, prog name, verdict, total instructions order).
> 
> This tool should be useful for validating various BPF verifier
> changes
> or even validating different kernel versions for regressions.
> 
> Here's an example for some of the heaviest selftests/bpf BPF object
> files:
> 
>   $ sudo ./veristat -s insns,file,prog
> {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*
> .linked3.o
>   File                                 
> Program                               Verdict  Duration, us  Total
> insns  Total states  Peak states
>   ------------------------------------  -----------------------------
> -------  -------  ------------  -----------  ------------  ----------
> -
>   loop3.linked3.o                      
> while_true                            failure        350990     
> 1000001          9663         9663
>   test_verif_scale3.linked3.o          
> balancer_ingress                      success        115244      
> 845499          8636         2141
>   test_verif_scale2.linked3.o          
> balancer_ingress                      success         77688      
> 773445          3048          788
>   pyperf600.linked3.o                  
> on_event                              success       2079872      
> 624585         30335        30241
>   pyperf600_nounroll.linked3.o         
> on_event                              success        353972      
> 568128         37101         2115
>   strobemeta.linked3.o                 
> on_event                              success        455230      
> 557149         15915        13537
>   test_verif_scale1.linked3.o          
> balancer_ingress                      success         89880      
> 554754          8636         2141
>   strobemeta_nounroll2.linked3.o       
> on_event                              success        433906      
> 501725         17087         1912
>   loop6.linked3.o                      
> trace_virtqueue_add_sgs               success        282205      
> 398057          8717          919
>   loop1.linked3.o                      
> nested_loops                          success        125630      
> 361349          5504         5504
>   pyperf180.linked3.o                  
> on_event                              success       2511740      
> 160398         11470        11446
>   pyperf100.linked3.o                  
> on_event                              success        744329       
> 87681          6213         6191
>   test_cls_redirect.linked3.o          
> cls_redirect                          success         54087       
> 78925          4782          903
>   strobemeta_subprogs.linked3.o        
> on_event                              success         57898       
> 65420          1954          403
>   test_cls_redirect_subprogs.linked3.o 
> cls_redirect                          success         54522       
> 64965          4619          958
>   strobemeta_nounroll1.linked3.o       
> on_event                              success         43313       
> 57240          1757          382
>   pyperf50.linked3.o                   
> on_event                              success        194355       
> 46378          3263         3241
>   profiler2.linked3.o                  
> tracepoint__syscalls__sys_enter_kill  success         23869       
> 43372          1423          542
>   pyperf_subprogs.linked3.o            
> on_event                              success         29179       
> 36358          2499         2499
>   profiler1.linked3.o                  
> tracepoint__syscalls__sys_enter_kill  success         13052       
> 27036          1946          936
>   profiler3.linked3.o                  
> tracepoint__syscalls__sys_enter_kill  success         21023       
> 26016          2186          915
>   profiler2.linked3.o                  
> kprobe__vfs_link                      success          5255       
> 13896           303          271
>   profiler1.linked3.o                  
> kprobe__vfs_link                      success          7792       
> 12687          1042         1041
>   profiler3.linked3.o                  
> kprobe__vfs_link                      success          7332       
> 10601           865          865
>   profiler2.linked3.o                  
> kprobe_ret__do_filp_open              success          3417        
> 8900           216          199
>   profiler2.linked3.o                  
> kprobe__vfs_symlink                   success          3548        
> 8775           203          186
>   pyperf_global.linked3.o              
> on_event                              success         10007        
> 7563           520          520
>   profiler3.linked3.o                  
> kprobe_ret__do_filp_open              success          4708        
> 6464           532          532
>   profiler1.linked3.o                  
> kprobe_ret__do_filp_open              success          3090        
> 6445           508          508
>   profiler3.linked3.o                  
> kprobe__vfs_symlink                   success          4477        
> 6358           521          521
>   profiler1.linked3.o                  
> kprobe__vfs_symlink                   success          3381        
> 6347           507          507
>   profiler2.linked3.o                  
> raw_tracepoint__sched_process_exec    success          2464        
> 5874           292          189
>   profiler3.linked3.o                  
> raw_tracepoint__sched_process_exec    success          2677        
> 4363           397          283
>   profiler2.linked3.o                  
> kprobe__proc_sys_write                success          1800        
> 4355           143          138
>   profiler1.linked3.o                  
> raw_tracepoint__sched_process_exec    success          1649        
> 4019           333          240
>   pyperf600_bpf_loop.linked3.o         
> on_event                              success          2711        
> 3966           306          306
>   profiler2.linked3.o                  
> raw_tracepoint__sched_process_exit    success          1234        
> 3138            83           66
>   profiler3.linked3.o                  
> kprobe__proc_sys_write                success          1755        
> 2623           223          223
>   profiler1.linked3.o                  
> kprobe__proc_sys_write                success          1222        
> 2456           193          193
>   loop2.linked3.o                      
> while_true                            success           608        
> 1783            57           30
>   profiler3.linked3.o                  
> raw_tracepoint__sched_process_exit    success           789        
> 1680           146          146
>   profiler1.linked3.o                  
> raw_tracepoint__sched_process_exit    success           592        
> 1526           133          133
>   strobemeta_bpf_loop.linked3.o        
> on_event                              success          1015        
> 1512           106          106
>   loop4.linked3.o                      
> combinations                          success           165         
> 524            18           17
>   profiler3.linked3.o                  
> raw_tracepoint__sched_process_fork    success           196         
> 299            25           25
>   profiler1.linked3.o                  
> raw_tracepoint__sched_process_fork    success           109         
> 265            19           19
>   profiler2.linked3.o                  
> raw_tracepoint__sched_process_fork    success           111         
> 265            19           19
>   loop5.linked3.o                      
> while_true                            success            47          
> 84             9            9
>   ------------------------------------  -----------------------------
> -------  -------  ------------  -----------  ------------  ----------
> -
> 
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
>  tools/testing/selftests/bpf/.gitignore |   1 +
>  tools/testing/selftests/bpf/Makefile   |   6 +-
>  tools/testing/selftests/bpf/veristat.c | 541
> +++++++++++++++++++++++++
>  3 files changed, 547 insertions(+), 1 deletion(-)
>  create mode 100644 tools/testing/selftests/bpf/veristat.c
> 
> diff --git a/tools/testing/selftests/bpf/.gitignore
> b/tools/testing/selftests/bpf/.gitignore
> index 3a8cb2404ea6..3b288562963e 100644
> --- a/tools/testing/selftests/bpf/.gitignore
> +++ b/tools/testing/selftests/bpf/.gitignore
> @@ -39,6 +39,7 @@ test_cpp
>  /tools
>  /runqslower
>  /bench
> +/veristat
>  *.ko
>  *.tmp
>  xskxceiver

[...]

> +
> +static int process_prog(const char *filename, struct bpf_object
> *obj, struct bpf_program *prog)
> +{
> +       const char *prog_name = bpf_program__name(prog);
> +       size_t buf_sz = sizeof(verif_log_buf);
> +       char *buf = verif_log_buf;
> +       struct verif_stats *stats;
> +       int err = 0;
> +       void *tmp;
> +
> +       tmp = realloc(env.prog_stats, (env.prog_stat_cnt + 1) *
> sizeof(*env.prog_stats));
> +       if (!tmp)
> +               return -ENOMEM;
> +       env.prog_stats = tmp;
> +       stats = &env.prog_stats[env.prog_stat_cnt++];
> +       memset(stats, 0, sizeof(*stats));
> +
> +       if (env.verbose) {
> +               buf_sz = 16 * 1024 * 1024;
> +               buf = malloc(buf_sz);
> +               if (!buf)
> +                       return -ENOMEM;
> +               bpf_program__set_log_buf(prog, buf, buf_sz);
> +               bpf_program__set_log_level(prog, 1 | 4); /* stats +
> log */
> +       } else {
> +               bpf_program__set_log_buf(prog, buf, buf_sz);
> +               bpf_program__set_log_level(prog, 4); /* only verifier
> stats */
> +       }
> +       verif_log_buf[0] = '\0';
> +
> +       err = bpf_object__load(obj);
> +
> +       stats->file_name = strdup(basename(filename));
> +       stats->prog_name = strdup(bpf_program__name(prog));
> +       stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure
> */
> +       parse_verif_log(buf, buf_sz, stats);
> +
> +       if (env.verbose) {
> +               printf("PROCESSING %s/%s, DURATION US: %ld, VERDICT:
> %s, VERIFIER LOG:\n%s\n",
> +                      filename, prog_name, stats->stats[DURATION],
> +                      err ? "failure" : "success", buf);
> +       }
> +
> +       if (verif_log_buf != buf)
> +               free(buf);
> +
> +       return 0;
> +};
> +
> +static int process_obj(const char *filename)
> +{
> +       struct bpf_object *obj = NULL, *tobj;
> +       struct bpf_program *prog, *tprog, *lprog;
> +       libbpf_print_fn_t old_libbpf_print_fn;
> +       LIBBPF_OPTS(bpf_object_open_opts, opts);
> +       int err = 0, prog_cnt = 0;
> +
> +       old_libbpf_print_fn = libbpf_set_print(libbpf_print_fn);
> +
> +       obj = bpf_object__open_file(filename, &opts);
> +       if (!obj) {
> +               err = -errno;
> +               fprintf(stderr, "Failed to open '%s': %d\n",
> filename, err);
> +               goto cleanup;
> +       }
> +
> +       bpf_object__for_each_program(prog, obj) {
> +               prog_cnt++;
> +       }
> +
> +       if (prog_cnt == 1) {
> +               prog = bpf_object__next_program(obj, NULL);
> +               bpf_program__set_autoload(prog, true);
> +               process_prog(filename, obj, prog);
> +               bpf_object__close(obj);
> +               goto cleanup;
> +       }
> +
> +       bpf_object__for_each_program(prog, obj) {
> +               const char *prog_name = bpf_program__name(prog);
> +
> +               tobj = bpf_object__open_file(filename, &opts);
> +               if (!tobj) {
> +                       err = -errno;
> +                       fprintf(stderr, "Failed to open '%s': %d\n",
> filename, err);
> +                       goto cleanup;
> +               }
> +
> +               bpf_object__for_each_program(tprog, tobj) {
> +                       const char *tprog_name =
> bpf_program__name(tprog);
> +
> +                       if (strcmp(prog_name, tprog_name) == 0) {
> +                               bpf_program__set_autoload(tprog,
> true);
> +                               lprog = tprog;
> +                       } else {
> +                               bpf_program__set_autoload(tprog,
> false);
> +                       }
> +               }
> +
> +               process_prog(filename, tobj, lprog);
> +               bpf_object__close(tobj);
> +       }

It leaks obj.

> > +
> +cleanup:
> +       libbpf_set_print(old_libbpf_print_fn);
> +       return err;
> +}
> +
> +static int cmp_stat(const struct verif_stats *s1, const struct
> verif_stats *s2,
> +                   enum stat_id id, bool asc)
> +{
> +       int cmp = 0;
> +
> +       switch (id) {
> +       case FILE_NAME:
> +               cmp = strcmp(s1->file_name, s2->file_name);
> +               break;
> +       case PROG_NAME:
> +               cmp = strcmp(s1->prog_name, s2->prog_name);
> +               break;
> +       case VERDICT:
> +       case DURATION:
> +       case TOTAL_INSNS:
> +       case TOTAL_STATES:
> +       case PEAK_STATES:
> +       case MAX_STATES_PER_INSN:
> +       case MARK_READ_MAX_LEN: {
> +               long v1 = s1->stats[id];
> +               long v2 = s2->stats[id];
> +
> +               if (v1 != v2)
> +                       cmp = v1 < v2 ? -1 : 1;
> +               break;
> +       }
> +       default:
> +               fprintf(stderr, "Unrecognized stat #%d\n", id);
> +               exit(1);
> +       }
> +
> +       return asc ? cmp : -cmp;
> +}
> +
> +static int cmp_prog_stats(const void *v1, const void *v2)
> +{
> +       const struct verif_stats *s1 = v1, *s2 = v2;
> +       int i, cmp;
> +
> +       for (i = 0; i < env.sort_spec.spec_cnt; i++) {
> +               cmp = cmp_stat(s1, s2, env.sort_spec.ids[i],
> env.sort_spec.asc[i]);
> +               if (cmp != 0)
> +                       return cmp;
> +       }
> +
> +       return 0;
> +}
> +
> +#define snappendf(dst, len, fmt,
> args...)                                      \
> +       len += snprintf(dst +
> len,                                              \
> +                             sizeof(dst) < len ? 0 : sizeof(dst) -
> len,        \
> +                             fmt, ##args)

Never been used?

> +
> +#define HEADER_CHAR '-'
> +#define COLUMN_SEP "  "
> +
> +static void output_headers(bool calc_len)
> +{
> +       int i, len;
> +
> +       for (i = 0; i < env.output_spec.spec_cnt; i++) {
> +               int id = env.output_spec.ids[i];
> +               int *max_len = &env.output_spec.lens[i];
> +
> +               if (calc_len) {
> +                       len = snprintf(NULL, 0, "%s",
> stat_defs[id].header);

Is there any reason to no use strlen()?
It would be ok to merge this block to one line since this function is
always called exactly once before output_stats() with calc_len being
true.  For example,

 *max_len = strlen(...)


> +                       if (len > *max_len)
> +                               *max_len = len;
> +               } else {
> +                       printf("%s%-*s", i == 0 ? "" : COLUMN_SEP, 
> *max_len, stat_defs[id].header);
> +               }
> +       }
> +
> +       if (!calc_len)
> +               printf("\n");
> +}
> +
> +static void output_lines(void)

lines? It is confusing for me since it always prints exactly one line.
How about something like output_sep_line()?

> +{
> +       int i, j, len;
> +
> +       for (i = 0; i < env.output_spec.spec_cnt; i++) {
> +               len = env.output_spec.lens[i];
> +
> +               printf("%s", i == 0 ? "" : COLUMN_SEP);
> +               for (j = 0; j < len; j++)
> +                       printf("%c", HEADER_CHAR);
> +       }
> +       printf("\n");
> +}
> +
> +static void output_stats(const struct verif_stats *s, bool calc_len)
> +{
> +       int i;
> +
> +       for (i = 0; i < env.output_spec.spec_cnt; i++) {
> +               int id = env.output_spec.ids[i];
> +               int *max_len = &env.output_spec.lens[i], len;
> +               const char *str = NULL;
> +               long val = 0;
> +
> +               switch (id) {
> +               case FILE_NAME:
> +                       str = s->file_name;
> +                       break;
> +               case PROG_NAME:
> +                       str = s->prog_name;
> +                       break;
> +               case VERDICT:
> +                       str = s->stats[VERDICT] ? "success" :
> "failure";
> +                       break;
> +               case DURATION:
> +               case TOTAL_INSNS:
> +               case TOTAL_STATES:
> +               case PEAK_STATES:
> +               case MAX_STATES_PER_INSN:
> +               case MARK_READ_MAX_LEN:
> +                       val = s->stats[id];
> +                       break;
> +               default:
> +                       fprintf(stderr, "Unrecognized stat #%d\n",
> id);
> +                       exit(1);
> +               }
> +               
> +               if (calc_len) {
> +                       if (str)
> +                               len = snprintf(NULL, 0, "%s", str);
> +                       else
> +                               len = snprintf(NULL, 0, "%ld", val);
> +                       if (len > *max_len)
> +                               *max_len = len;
> +               } else {
> +                       if (str)
> +                               printf("%s%-*s", i == 0 ? "" :
> COLUMN_SEP, *max_len, str);
> +                       else
> +                               printf("%s%*ld", i == 0 ? "" :
> COLUMN_SEP,  *max_len, val);
> +               }
> +       }
> +
> +       if (!calc_len)
> +               printf("\n");
> +}
> +
> +int main(int argc, char **argv)
> +{
> +       static const struct argp argp = {
> +               .options = opts,
> +               .parser = parse_arg,
> +               .doc = argp_program_doc,
> +       };
> +       int err = 0, i;
> +
> +       if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
> +               return 1;
> +
> +       if (env.filename_cnt == 0) {
> +               fprintf(stderr, "Please provide path to BPF object
> file!\n");
> +               argp_help(&argp, stderr, ARGP_HELP_USAGE,
> "veristat");
> +               return 1;
> +       }
> +
> +       if (env.output_spec.spec_cnt == 0)
> +               env.output_spec = default_output_spec;
> +       if (env.sort_spec.spec_cnt == 0)
> +               env.sort_spec = default_sort_spec;
> +
> +       for (i = 0; i < env.filename_cnt; i++) {
> +               err = process_obj(env.filenames[i]);
> +               if (err) {
> +                       fprintf(stderr, "Failed to process '%s':
> %d\n", env.filenames[i], err);
> +                       goto cleanup;
> +               }
> +       }
> +
> +       qsort(env.prog_stats, env.prog_stat_cnt,
> sizeof(*env.prog_stats), cmp_prog_stats);
> +
> +       /* calculate column widths */
> +       output_headers(true);
> +       for (i = 0; i < env.prog_stat_cnt; i++) {
> +               output_stats(&env.prog_stats[i], true);
> +       }
> +
> +       /* actually output the table */
> +       output_headers(false);
> +       output_lines();
> +       for (i = 0; i < env.prog_stat_cnt; i++) {
> +               output_stats(&env.prog_stats[i], false);
> +       }
> +       output_lines();
> +       printf("\n");
> +
> +       printf("Done. Processed %d object files, %d programs.\n",
> +              env.filename_cnt, env.prog_stat_cnt);
> +
> +cleanup:
> +       for (i = 0; i < env.prog_stat_cnt; i++) {
> +               free(env.prog_stats[i].file_name);
> +               free(env.prog_stats[i].prog_name);
> +       }
> +       free(env.prog_stats);
> +       for (i = 0; i < env.filename_cnt; i++)
> +               free(env.filenames[i]);
> +       free(env.filenames);
> +       return -err;
> +}


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-27 23:53   ` KP Singh
@ 2022-08-29 14:50     ` Daniel Borkmann
  2022-09-09 19:24       ` Andrii Nakryiko
  2022-09-09 19:04     ` Andrii Nakryiko
  1 sibling, 1 reply; 12+ messages in thread
From: Daniel Borkmann @ 2022-08-29 14:50 UTC (permalink / raw)
  To: KP Singh, Andrii Nakryiko, Quentin Monnet; +Cc: bpf, ast, kernel-team

On 8/28/22 1:53 AM, KP Singh wrote:
> On Sat, Aug 27, 2022 at 1:15 AM Andrii Nakryiko <andrii@kernel.org> wrote:
>>
>> Add a small tool, veristat, that allows mass-verification of
>> a set of *libbpf-compatible* BPF ELF object files. For each such object
>> file, veristat will attempt to verify each BPF program *individually*.
>> Regardless of success or failure, it parses BPF verifier stats and
>> outputs them in human-readable table format. In the future we can also
>> add CSV and JSON output for more scriptable post-processing, if necessary.
>>
>> veristat allows to specify a set of stats that should be output and
>> ordering between multiple objects and files (e.g., so that one can
>> easily order by total instructions processed, instead of default file
>> name, prog name, verdict, total instructions order).
>>
>> This tool should be useful for validating various BPF verifier changes
>> or even validating different kernel versions for regressions.
> 
> Cool stuff!

+1, out of curiosity, did you try with different kernels to see the deltas?

> I think this would be useful for cases beyond these (i.e. for users to get
> stats about the verifier in general) and it's worth thinking if this should
> be built into bpftool?
> 
>>
>> Here's an example for some of the heaviest selftests/bpf BPF object
>> files:
>>
>>    $ sudo ./veristat -s insns,file,prog {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*.linked3.o
>>    File                                  Program                               Verdict  Duration, us  Total insns  Total states  Peak states
>>    ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------
>>    loop3.linked3.o                       while_true                            failure        350990      1000001          9663         9663
> 
> [...]

nit: Looks like CI on gcc is bailing:

https://github.com/kernel-patches/bpf/runs/8072477251?check_suite_focus=true

[...]
     INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/skel_internal.h
   In file included from /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf.h:20,
     INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf_version.h
                    from veristat.c:17:
   /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf_common.h:13:10: fatal error: libbpf_version.h: No such file or directory
      13 | #include "libbpf_version.h"
         |          ^~~~~~~~~~~~~~~~~~
   compilation terminated.
     INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/usdt.bpf.h
     HOSTCC  /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/build/libbpf/fixdep.o
   make: *** [Makefile:165: /tmp/work/bpf/bpf/tools/testing/selftests/bpf/veristat.o] Error 1
   make: *** Waiting for unfinished jobs....

I wonder, to detect regressions in pruning behavior, could we add a test_progs subtest to load
selected obj files and compare before/after 'verified insns' numbers? The workflow probably
makes this a bit hard to run with a kernel before this change, but maybe it could be a starting
point where we have a checked-in file containing current numbers, and e.g. if the new change
crosses a threshold of current +10% then the test could fail?

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-27 23:53   ` KP Singh
  2022-08-29 14:50     ` Daniel Borkmann
@ 2022-09-09 19:04     ` Andrii Nakryiko
  1 sibling, 0 replies; 12+ messages in thread
From: Andrii Nakryiko @ 2022-09-09 19:04 UTC (permalink / raw)
  To: KP Singh; +Cc: Andrii Nakryiko, Quentin Monnet, bpf, ast, daniel, kernel-team

On Sat, Aug 27, 2022 at 4:53 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Sat, Aug 27, 2022 at 1:15 AM Andrii Nakryiko <andrii@kernel.org> wrote:
> >
> > Add a small tool, veristat, that allows mass-verification of
> > a set of *libbpf-compatible* BPF ELF object files. For each such object
> > file, veristat will attempt to verify each BPF program *individually*.
> > Regardless of success or failure, it parses BPF verifier stats and
> > outputs them in human-readable table format. In the future we can also
> > add CSV and JSON output for more scriptable post-processing, if necessary.
> >
> > veristat allows to specify a set of stats that should be output and
> > ordering between multiple objects and files (e.g., so that one can
> > easily order by total instructions processed, instead of default file
> > name, prog name, verdict, total instructions order).
> >
> > This tool should be useful for validating various BPF verifier changes
> > or even validating different kernel versions for regressions.
>
> Cool stuff!
>
> I think this would be useful for cases beyond these (i.e. for users to get
> stats about the verifier in general) and it's worth thinking if this should
> be built into bpftool?

I think it's a bit premature to put this into bpftool (IMO, I don't
like kitchen sink approach to libraries and tools), let's see how this
is used in practice first.

But yes, my plan was to expose more internal stats from verifier
through using attached BPF program. I even have BPF skeleton wired
locally, but right now just for test grab duration (which we already
get throuhg verifier log, so it's useless right now). But with BPF
program we can fetch internal BPF verifier state during verification.

I also plan to add a bit more customizability as to which subsets of
programs to run, similar to how test_progs allow to filter tests and
subtests, but here we'll have objects and progs. So anyway, this is a
very first version that I got time to clean up a bit and post. I plan
at least few more,  so let's hold off on putting this into bpftool (at
least yet).

>
> >
> > Here's an example for some of the heaviest selftests/bpf BPF object
> > files:
> >
> >   $ sudo ./veristat -s insns,file,prog {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*.linked3.o
> >   File                                  Program                               Verdict  Duration, us  Total insns  Total states  Peak states
> >   ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------
> >   loop3.linked3.o                       while_true                            failure        350990      1000001          9663         9663
>
> [...]
>
> > --
> > 2.30.2
> >

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-29  6:02   ` Kui-Feng Lee
@ 2022-09-09 19:18     ` Andrii Nakryiko
  0 siblings, 0 replies; 12+ messages in thread
From: Andrii Nakryiko @ 2022-09-09 19:18 UTC (permalink / raw)
  To: Kui-Feng Lee; +Cc: daniel, ast, andrii, bpf, Kernel Team

On Sun, Aug 28, 2022 at 11:02 PM Kui-Feng Lee <kuifeng@fb.com> wrote:
>
> On Fri, 2022-08-26 at 16:15 -0700, Andrii Nakryiko wrote:
> > Add a small tool, veristat, that allows mass-verification of
> > a set of *libbpf-compatible* BPF ELF object files. For each such
> > object
> > file, veristat will attempt to verify each BPF program
> > *individually*.
> > Regardless of success or failure, it parses BPF verifier stats and
> > outputs them in human-readable table format. In the future we can
> > also
> > add CSV and JSON output for more scriptable post-processing, if
> > necessary.
> >
> > veristat allows to specify a set of stats that should be output and
> > ordering between multiple objects and files (e.g., so that one can
> > easily order by total instructions processed, instead of default file
> > name, prog name, verdict, total instructions order).
> >
> > This tool should be useful for validating various BPF verifier
> > changes
> > or even validating different kernel versions for regressions.
> >
> > Here's an example for some of the heaviest selftests/bpf BPF object
> > files:
> >
> >   $ sudo ./veristat -s insns,file,prog
> > {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*
> > .linked3.o
> >   File
> > Program                               Verdict  Duration, us  Total
> > insns  Total states  Peak states

[...]

> >
> > Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> > ---
> >  tools/testing/selftests/bpf/.gitignore |   1 +
> >  tools/testing/selftests/bpf/Makefile   |   6 +-
> >  tools/testing/selftests/bpf/veristat.c | 541
> > +++++++++++++++++++++++++
> >  3 files changed, 547 insertions(+), 1 deletion(-)
> >  create mode 100644 tools/testing/selftests/bpf/veristat.c
> >
> > diff --git a/tools/testing/selftests/bpf/.gitignore
> > b/tools/testing/selftests/bpf/.gitignore
> > index 3a8cb2404ea6..3b288562963e 100644
> > --- a/tools/testing/selftests/bpf/.gitignore
> > +++ b/tools/testing/selftests/bpf/.gitignore
> > @@ -39,6 +39,7 @@ test_cpp
> >  /tools
> >  /runqslower
> >  /bench
> > +/veristat
> >  *.ko
> >  *.tmp
> >  xskxceiver
>
> [...]
>
> > +
> > +static int process_prog(const char *filename, struct bpf_object
> > *obj, struct bpf_program *prog)
> > +{

[...]

> > +               bpf_object__for_each_program(tprog, tobj) {
> > +                       const char *tprog_name =
> > bpf_program__name(tprog);
> > +
> > +                       if (strcmp(prog_name, tprog_name) == 0) {
> > +                               bpf_program__set_autoload(tprog,
> > true);
> > +                               lprog = tprog;
> > +                       } else {
> > +                               bpf_program__set_autoload(tprog,
> > false);
> > +                       }
> > +               }
> > +
> > +               process_prog(filename, tobj, lprog);
> > +               bpf_object__close(tobj);
> > +       }
>
> It leaks obj.

good catch, fixed.

>
> > > +
> > +cleanup:
> > +       libbpf_set_print(old_libbpf_print_fn);
> > +       return err;
> > +}
> > +

[...]

> > +
> > +#define snappendf(dst, len, fmt,
> > args...)                                      \
> > +       len += snprintf(dst +
> > len,                                              \
> > +                             sizeof(dst) < len ? 0 : sizeof(dst) -
> > len,        \
> > +                             fmt, ##args)
>
> Never been used?

yep, dropped

>
> > +
> > +#define HEADER_CHAR '-'
> > +#define COLUMN_SEP "  "
> > +
> > +static void output_headers(bool calc_len)
> > +{
> > +       int i, len;
> > +
> > +       for (i = 0; i < env.output_spec.spec_cnt; i++) {
> > +               int id = env.output_spec.ids[i];
> > +               int *max_len = &env.output_spec.lens[i];
> > +
> > +               if (calc_len) {
> > +                       len = snprintf(NULL, 0, "%s",
> > stat_defs[id].header);
>
> Is there any reason to no use strlen()?

to keep it consistent with other snprintf() uses for calc_len case

> It would be ok to merge this block to one line since this function is
> always called exactly once before output_stats() with calc_len being
> true.  For example,
>
>  *max_len = strlen(...)
>

yes, but I wanted to keep it consistent and not have to keep track of
ordering dependencies. So I'd prefer to keep it as is.

>
> > +                       if (len > *max_len)
> > +                               *max_len = len;
> > +               } else {
> > +                       printf("%s%-*s", i == 0 ? "" : COLUMN_SEP,
> > *max_len, stat_defs[id].header);
> > +               }
> > +       }
> > +
> > +       if (!calc_len)
> > +               printf("\n");
> > +}
> > +
> > +static void output_lines(void)
>
> lines? It is confusing for me since it always prints exactly one line.
> How about something like output_sep_line()?

lines refers to "---------" under headers and at the bottom, so
multiple of them. sep (for separator) is also a bit misleading. I'll
call it output_header_underlines().

>
> > +{
> > +       int i, j, len;
> > +
> > +       for (i = 0; i < env.output_spec.spec_cnt; i++) {
> > +               len = env.output_spec.lens[i];
> > +
> > +               printf("%s", i == 0 ? "" : COLUMN_SEP);
> > +               for (j = 0; j < len; j++)
> > +                       printf("%c", HEADER_CHAR);
> > +       }
> > +       printf("\n");
> > +}
> > +

[...]

note: it's a good idea to trim irrelevant parts of the email to keep
it shorter and easier to read, if patch is long

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files
  2022-08-29 14:50     ` Daniel Borkmann
@ 2022-09-09 19:24       ` Andrii Nakryiko
  0 siblings, 0 replies; 12+ messages in thread
From: Andrii Nakryiko @ 2022-09-09 19:24 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: KP Singh, Andrii Nakryiko, Quentin Monnet, bpf, ast, kernel-team

On Mon, Aug 29, 2022 at 7:50 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On 8/28/22 1:53 AM, KP Singh wrote:
> > On Sat, Aug 27, 2022 at 1:15 AM Andrii Nakryiko <andrii@kernel.org> wrote:
> >>
> >> Add a small tool, veristat, that allows mass-verification of
> >> a set of *libbpf-compatible* BPF ELF object files. For each such object
> >> file, veristat will attempt to verify each BPF program *individually*.
> >> Regardless of success or failure, it parses BPF verifier stats and
> >> outputs them in human-readable table format. In the future we can also
> >> add CSV and JSON output for more scriptable post-processing, if necessary.
> >>
> >> veristat allows to specify a set of stats that should be output and
> >> ordering between multiple objects and files (e.g., so that one can
> >> easily order by total instructions processed, instead of default file
> >> name, prog name, verdict, total instructions order).
> >>
> >> This tool should be useful for validating various BPF verifier changes
> >> or even validating different kernel versions for regressions.
> >
> > Cool stuff!
>
> +1, out of curiosity, did you try with different kernels to see the deltas?

Nope, not yet, barely got the code to the current state before leaving
on vacation. But I thought about using this to track regressions and
improvements over time as we make changes to BPF verifier. I was
thinking to have not just table human-readable output, but also csv
and/or json, so that we can build some sort of automation to run this
periodically (or even in BPF CI for each patch set) and yell about
significant changes. veristat changes are easy, but someone will need
to build this sort of automation. There are projects like rustc (Rust
compiler) that have this sort of thing very nicely formalized, we
might want to do that for Clang + BPF verifier changes as well.

>
> > I think this would be useful for cases beyond these (i.e. for users to get
> > stats about the verifier in general) and it's worth thinking if this should
> > be built into bpftool?
> >
> >>
> >> Here's an example for some of the heaviest selftests/bpf BPF object
> >> files:
> >>
> >>    $ sudo ./veristat -s insns,file,prog {pyperf,loop,test_verif_scale,strobemeta,test_cls_redirect,profiler}*.linked3.o
> >>    File                                  Program                               Verdict  Duration, us  Total insns  Total states  Peak states
> >>    ------------------------------------  ------------------------------------  -------  ------------  -----------  ------------  -----------
> >>    loop3.linked3.o                       while_true                            failure        350990      1000001          9663         9663
> >
> > [...]
>
> nit: Looks like CI on gcc is bailing:
>
> https://github.com/kernel-patches/bpf/runs/8072477251?check_suite_focus=true
>
> [...]
>      INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/skel_internal.h
>    In file included from /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf.h:20,
>      INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf_version.h
>                     from veristat.c:17:
>    /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/libbpf_common.h:13:10: fatal error: libbpf_version.h: No such file or directory
>       13 | #include "libbpf_version.h"
>          |          ^~~~~~~~~~~~~~~~~~

hm... Makefile dependencies not correct? I'll check and fix.

>    compilation terminated.
>      INSTALL /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/include/bpf/usdt.bpf.h
>      HOSTCC  /tmp/work/bpf/bpf/tools/testing/selftests/bpf/tools/build/libbpf/fixdep.o
>    make: *** [Makefile:165: /tmp/work/bpf/bpf/tools/testing/selftests/bpf/veristat.o] Error 1
>    make: *** Waiting for unfinished jobs....
>
> I wonder, to detect regressions in pruning behavior, could we add a test_progs subtest to load
> selected obj files and compare before/after 'verified insns' numbers? The workflow probably
> makes this a bit hard to run with a kernel before this change, but maybe it could be a starting
> point where we have a checked-in file containing current numbers, and e.g. if the new change
> crosses a threshold of current +10% then the test could fail?
>

Heh, wrote above before reading this. But yes, if add CSV output and
add some sort of baseline upload of last stats, we should be able to
do the diff and yell about major regressions. I also want to add
test_progs's test/subtest glob selection logic for object/program
combo so that we can narrow down list of objects and progs within them
to test. Most of programs are trivial and just pollute output, so
having shorter list is better. And then we can check in representative
list into selftests/bpf, just like we do with DENYLIST/ALLOWLIST for
BPF CI.

But you know, baby steps :)


> Thanks,
> Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2022-09-09 19:24 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-26 23:15 [PATCH bpf-next 0/3] BPF mass-verification veristat tool Andrii Nakryiko
2022-08-26 23:15 ` [PATCH bpf-next 1/3] selftests/bpf: fix test_verif_scale{1,3} SEC() annotations Andrii Nakryiko
2022-08-27 23:43   ` KP Singh
2022-08-26 23:15 ` [PATCH bpf-next 2/3] libbpf: fix crash if SEC("freplace") programs don't have attach_prog_fd set Andrii Nakryiko
2022-08-27 23:45   ` KP Singh
2022-08-26 23:15 ` [PATCH bpf-next 3/3] selftests/bpf: add veristat tool for mass-verifying BPF object files Andrii Nakryiko
2022-08-27 23:53   ` KP Singh
2022-08-29 14:50     ` Daniel Borkmann
2022-09-09 19:24       ` Andrii Nakryiko
2022-09-09 19:04     ` Andrii Nakryiko
2022-08-29  6:02   ` Kui-Feng Lee
2022-09-09 19:18     ` Andrii Nakryiko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.