Commit 1c0b5059 authored by Alexis Lothoré (eBPF Foundation)'s avatar Alexis Lothoré (eBPF Foundation) Committed by Alexei Starovoitov
Browse files

selftests/bpf: convert test_bpftool_metadata.sh into test_progs framework



The test_bpftool_metadata.sh script validates that bpftool properly
returns in its ouptput any metadata generated by bpf programs through
some .rodata sections.

Port this test to the test_progs framework so that it can be executed
automatically in CI. The new test, similarly to the former script,
checks that valid data appears both for textual output and json output,
as well as for both data not used at all and used data. For the json
check part, the expected json string is hardcoded to avoid bringing a
new external dependency (eg: a json deserializer) for test_progs.
As the test is now converted into test_progs, remove the former script.

The newly converted test brings two new subtests:

  #37/1    bpftool_metadata/metadata_unused:OK
  #37/2    bpftool_metadata/metadata_used:OK
  #37      bpftool_metadata:OK
  Summary: 1/2 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: default avatarAlexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
Link: https://lore.kernel.org/r/20260123-bpftool-tests-v4-2-a6653a7f28e7@bootlin.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent f21fae57
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -109,7 +109,6 @@ TEST_PROGS := test_kmod.sh \
	test_bpftool_build.sh \
	test_bpftool.sh \
	test_bpftool_map.sh \
	test_bpftool_metadata.sh \
	test_doc_build.sh \
	test_xsk.sh \
	test_xdp_features.sh
+144 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
#include <bpftool_helpers.h>
#include <test_progs.h>
#include <linux/bpf.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdbool.h>

#define BPFFS_DIR	"/sys/fs/bpf/test_metadata"
#define BPFFS_USED	BPFFS_DIR "/used"
#define BPFFS_UNUSED	BPFFS_DIR "/unused"

#define BPF_FILE_USED		"metadata_used.bpf.o"
#define BPF_FILE_UNUSED		"metadata_unused.bpf.o"
#define METADATA_MAP_NAME	"metadata.rodata"

#define MAX_BPFTOOL_OUTPUT_LEN	(64*1024)

#define MAX_TOKENS_TO_CHECK	3
static char output[MAX_BPFTOOL_OUTPUT_LEN];

struct test_desc {
	char *name;
	char *bpf_prog;
	char *bpffs_path;
	char *expected_output[MAX_TOKENS_TO_CHECK];
	char *expected_output_json[MAX_TOKENS_TO_CHECK];
	char *metadata_map_name;
};

static int setup(struct test_desc *test)
{
	return mkdir(BPFFS_DIR, 0700);
}

static void cleanup(struct test_desc *test)
{
	unlink(test->bpffs_path);
	rmdir(BPFFS_DIR);
}

static int check_metadata(char *buf, char * const *tokens, int count)
{
	int i;

	for (i = 0; i < count && tokens[i]; i++)
		if (!strstr(buf, tokens[i]))
			return 1;

	return 0;
}

static void run_test(struct test_desc *test)
{
	int ret;
	char cmd[MAX_BPFTOOL_CMD_LEN];

	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog load %s %s",
			test->bpf_prog, test->bpffs_path);
	if (!ASSERT_GT(ret, 0, "format prog insert command"))
		return;
	ret = run_bpftool_command(cmd);
	if (!ASSERT_OK(ret, "load program"))
		return;

	/* Check output with default format */
	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog show pinned %s",
		       test->bpffs_path);
	if (!ASSERT_GT(ret, 0, "format pinned prog check command"))
		return;
	ret = get_bpftool_command_output(cmd, output,
			MAX_BPFTOOL_OUTPUT_LEN);
	if (ASSERT_OK(ret, "get program info")) {
		ret = check_metadata(output, test->expected_output,
				ARRAY_SIZE(test->expected_output));
		ASSERT_OK(ret, "find metadata");
	}

	/* Check output with json format */
	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog -j show pinned %s",
		       test->bpffs_path);
	if (!ASSERT_GT(ret, 0, "format pinned prog check command in json"))
		return;
	ret = get_bpftool_command_output(cmd, output,
					 MAX_BPFTOOL_OUTPUT_LEN);
	if (ASSERT_OK(ret, "get program info in json")) {
		ret = check_metadata(output, test->expected_output_json,
				ARRAY_SIZE(test->expected_output_json));
		ASSERT_OK(ret, "find metadata in json");
	}

	/* Check that the corresponding map can be found and accessed */
	ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map show name %s",
		       test->metadata_map_name);
	if (!ASSERT_GT(ret, 0, "format map check command"))
		return;
	ASSERT_OK(run_bpftool_command(cmd), "access metadata map");
}

static struct test_desc tests[] = {
	{
		.name = "metadata_unused",
		.bpf_prog = BPF_FILE_UNUSED,
		.bpffs_path = BPFFS_UNUSED,
		.expected_output = {
			"a = \"foo\"",
			"b = 1"
		},
		.expected_output_json = {
			"\"metadata\":{\"a\":\"foo\",\"b\":1}"
		},
		.metadata_map_name = METADATA_MAP_NAME
	},
	{
		.name = "metadata_used",
		.bpf_prog = BPF_FILE_USED,
		.bpffs_path = BPFFS_USED,
		.expected_output = {
			"a = \"bar\"",
			"b = 2"
		},
		.expected_output_json = {
			"\"metadata\":{\"a\":\"bar\",\"b\":2}"
		},
		.metadata_map_name = METADATA_MAP_NAME
	}
};
static const int tests_count = ARRAY_SIZE(tests);

void test_bpftool_metadata(void)
{
	int i;

	for (i = 0; i < tests_count; i++) {
		if (!test__start_subtest(tests[i].name))
			continue;
		if (ASSERT_OK(setup(&tests[i]), "setup bpffs pin dir")) {
			run_test(&tests[i]);
			cleanup(&tests[i]);
		}
	}
}
+0 −85
Original line number Diff line number Diff line
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0

# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4

BPF_FILE_USED="metadata_used.bpf.o"
BPF_FILE_UNUSED="metadata_unused.bpf.o"

TESTNAME=bpftool_metadata
BPF_FS=$(awk '$3 == "bpf" {print $2; exit}' /proc/mounts)
BPF_DIR=$BPF_FS/test_$TESTNAME

_cleanup()
{
	set +e
	rm -rf $BPF_DIR 2> /dev/null
}

cleanup_skip()
{
	echo "selftests: $TESTNAME [SKIP]"
	_cleanup

	exit $ksft_skip
}

cleanup()
{
	if [ "$?" = 0 ]; then
		echo "selftests: $TESTNAME [PASS]"
	else
		echo "selftests: $TESTNAME [FAILED]"
	fi
	_cleanup
}

if [ $(id -u) -ne 0 ]; then
	echo "selftests: $TESTNAME [SKIP] Need root privileges"
	exit $ksft_skip
fi

if [ -z "$BPF_FS" ]; then
	echo "selftests: $TESTNAME [SKIP] Could not run test without bpffs mounted"
	exit $ksft_skip
fi

if ! bpftool version > /dev/null 2>&1; then
	echo "selftests: $TESTNAME [SKIP] Could not run test without bpftool"
	exit $ksft_skip
fi

set -e

trap cleanup_skip EXIT

mkdir $BPF_DIR

trap cleanup EXIT

bpftool prog load $BPF_FILE_UNUSED $BPF_DIR/unused

METADATA_PLAIN="$(bpftool prog)"
echo "$METADATA_PLAIN" | grep 'a = "foo"' > /dev/null
echo "$METADATA_PLAIN" | grep 'b = 1' > /dev/null

bpftool prog --json | grep '"metadata":{"a":"foo","b":1}' > /dev/null

bpftool map | grep 'metadata.rodata' > /dev/null

rm $BPF_DIR/unused

bpftool prog load $BPF_FILE_USED $BPF_DIR/used

METADATA_PLAIN="$(bpftool prog)"
echo "$METADATA_PLAIN" | grep 'a = "bar"' > /dev/null
echo "$METADATA_PLAIN" | grep 'b = 2' > /dev/null

bpftool prog --json | grep '"metadata":{"a":"bar","b":2}' > /dev/null

bpftool map | grep 'metadata.rodata' > /dev/null

rm $BPF_DIR/used

exit 0