selftests/bpf: Exercise veristat filtering logic in a selftest

Test cases for veristat file/prog name filtering logic.
Check various formulations for any (*foo*), file (*foo*/),
prog (/bar) and file/prog (*foo*/bar) filters, alongside
erroneous filters and mixed allow/deny filter expressions.

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260811-veristat-filter-fix-v2-2-6c234c4cd6ef@gmail.com
This commit is contained in:
Eduard Zingerman
2026-08-11 23:05:43 -07:00
committed by Andrii Nakryiko
parent 98d309ec81
commit 6c034d962b
3 changed files with 135 additions and 0 deletions

View File

@@ -37,6 +37,14 @@ static struct fixture *init_fixture(void)
return fix;
}
static void read_output(struct fixture *fix)
{
ssize_t len = pread(fix->fd, fix->output, fix->sz - 1, 0);
fix->output[len < 0 ? 0 : len] = 0;
ASSERT_GE(len, 0, "pread");
}
static void teardown_fixture(struct fixture *fix)
{
free(fix->output);
@@ -230,6 +238,97 @@ static void test_no_array_index_for_array(void)
teardown_fixture(fix);
}
/*
* Name filter tests below run veristat on veristat_foo.bpf.o and
* veristat_bar.bpf.o, both defining programs 'foo', 'bar' and 'buz'.
* Every entry describes a single (filters, file, prog) combination and
* tells whether that program is expected in the veristat output:
* 'true' if it is, 'false' if it is not and -1 if veristat is expected
* to reject the filter.
*/
#define FILTER_OBJS "veristat_foo.bpf.o veristat_bar.bpf.o"
static const struct name_filter_case {
const char *filters;
const char *file;
const char *prog;
int included;
} name_filter_cases[] = {
/* no filters, every program is processed */
{ "", "foo", "foo", true },
{ "", "foo", "bar", true },
{ "", "foo", "buz", true },
{ "", "bar", "foo", true },
{ "", "bar", "bar", true },
{ "", "bar", "buz", true },
/* deny filters */
{ "-f '!*foo*'", "foo", "bar", false },
{ "-f '!*foo*'", "bar", "foo", false },
{ "-f '!*foo*'", "bar", "bar", true },
{ "-f '!*foo*/bar'", "foo", "bar", false },
{ "-f '!*foo*/bar'", "foo", "buz", true },
{ "-f '!*foo*/bar'", "bar", "bar", true },
{ "-f '!*foo*/'", "foo", "bar", false },
{ "-f '!*foo*/'", "bar", "bar", true },
{ "-f '!/bar'", "foo", "bar", false },
{ "-f '!/bar'", "foo", "foo", true },
{ "-f '!/'", "foo", "bar", -1 },
{ "-f '!'", "foo", "bar", -1 },
/* allow filters */
{ "-f '*foo*'", "foo", "bar", true },
{ "-f '*foo*'", "bar", "foo", true },
{ "-f '*foo*'", "bar", "bar", false },
{ "-f '*foo*/bar'", "foo", "bar", true },
{ "-f '*foo*/bar'", "foo", "buz", false },
{ "-f '*foo*/bar'", "bar", "bar", false },
{ "-f '*foo*/'", "foo", "bar", true },
{ "-f '*foo*/'", "bar", "bar", false },
{ "-f '/bar'", "foo", "bar", true },
{ "-f '/bar'", "foo", "foo", false },
{ "-f '/'", "foo", "bar", -1 },
{ "-f ''", "foo", "bar", -1 },
/* allow and deny filters combined */
{ "-f '*foo*/' -f '!/bar'", "foo", "foo", true },
{ "-f '*foo*/' -f '!/bar'", "foo", "bar", false },
{ "-f '*foo*/' -f '!/bar'", "bar", "foo", false },
};
static void test_name_filters(void)
{
struct fixture *fix = init_fixture();
const struct name_filter_case *t;
char cmd[512], row[64], name[128];
int i, err;
for (i = 0; i < ARRAY_SIZE(name_filter_cases); i++) {
t = &name_filter_cases[i];
/* stderr is merged with stdout in order to catch error messages */
snprintf(cmd, sizeof(cmd), "%s " FILTER_OBJS " -q -o csv -e file,prog %s > %s 2>&1",
fix->veristat, t->filters, fix->tmpfile);
err = system(cmd);
read_output(fix);
snprintf(row, sizeof(row), "veristat_%s.bpf.o,%s", t->file, t->prog);
snprintf(name, sizeof(name), "veristat %s: %s", t->filters, row);
switch (t->included) {
case true:
ASSERT_OK(err, name);
ASSERT_HAS_SUBSTR(fix->output, row, name);
break;
case false:
ASSERT_OK(err, name);
ASSERT_FALSE(!!strstr(fix->output, row), name);
break;
case -1:
ASSERT_NEQ(err, 0, name);
ASSERT_HAS_SUBSTR(fix->output, "Invalid filter", name);
break;
}
}
teardown_fixture(fix);
}
void test_veristat(void)
{
if (test__start_subtest("set_global_vars_succeeds"))
@@ -256,6 +355,8 @@ void test_veristat(void)
if (test__start_subtest("test_no_array_index_for_array"))
test_no_array_index_for_array();
if (test__start_subtest("name_filters"))
test_name_filters();
}
#undef __CHECK_STR

View File

@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include "veristat_foo.c"

View File

@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
/*
* Programs below exist only to exercise veristat's -f name filters,
* their bodies are irrelevant, only the names matter.
* This file is also included by veristat_bar.c, so that the same set of
* program names is available in two differently named object files.
*/
SEC("socket")
int foo(void *ctx)
{
return 0;
}
SEC("socket")
int bar(void *ctx)
{
return 0;
}
SEC("socket")
int buz(void *ctx)
{
return 0;
}
char _license[] SEC("license") = "GPL";