From: Eduard Zingerman <eddyz87@gmail.com>
To: bpf@vger.kernel.org, ast@kernel.org
Cc: andrii@kernel.org, daniel@iogearbox.net, kernel-team@fb.com,
yhs@fb.com, arnaldo.melo@gmail.com,
Eduard Zingerman <eddyz87@gmail.com>
Subject: [RFC bpf-next 10/12] selftests/bpf: Script to verify uapi headers usage with vmlinux.h
Date: Wed, 26 Oct 2022 01:27:59 +0300 [thread overview]
Message-ID: <20221025222802.2295103-11-eddyz87@gmail.com> (raw)
In-Reply-To: <20221025222802.2295103-1-eddyz87@gmail.com>
A script to test header guards in vmlinux.h by compiling a simple C
snippet for a set of selected UAPI headers. The snippet being
compiled looks as follows:
#include <some_uapi_header.h>
#include "vmlinux.h"
__attribute__((section("tc"), used))
int syncookie_tc(struct __sk_buff *skb) { return 0; }
If header guards are placed correctly in vmlinux.h the snippet
should compile w/o errors.
The list of known good headers is supposed to be located in
`tools/testing/selftests/bpf/good_uapi_headers.txt` added as a
separate commit.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
| 197 ++++++++++++++++++
1 file changed, 197 insertions(+)
create mode 100755 tools/testing/selftests/bpf/test_uapi_headers.py
--git a/tools/testing/selftests/bpf/test_uapi_headers.py b/tools/testing/selftests/bpf/test_uapi_headers.py
new file mode 100755
index 000000000000..1740c4fe0625
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_uapi_headers.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+# A script to test header guards in vmlinux.h by compiling a simple C
+# snippet for a set of selected UAPI headers. The snippet being
+# compiled looks as follows:
+#
+# #include <some_uapi_header.h>
+# #include "vmlinux.h"
+#
+# __attribute__((section("tc"), used))
+# int syncookie_tc(struct __sk_buff *skb) { return 0; }
+#
+# If header guards are placed correctly in vmlinux.h the snippet
+# should compile w/o errors.
+#
+# The script could be used in two modes:
+# - interactive BPF testing and CI;
+# - debug mode.
+#
+# * Interactive BPF testing and CI
+#
+# Run script as follows:
+#
+# ./test_uapi_headers.py
+#
+# In this mode the following actions are performed:
+# - kernel headers are installed to a temporary directory;
+# - a list of known good uapi headers is read from ./good_uapi_headers.txt;
+# - the snippet above is compiled by clang using BPF target for each header;
+# - if shell is interactive the progress / ETA are reported during execution;
+# - pass / fail statistics is reported in the end;
+# - headers temporary directory is deleted;
+# - script exit code is 0 if snippet could be compiled for all headers.
+#
+# The vmlinux.h processing time is significant (~700ms using Intel i7-4710HQ),
+# thus the headers are processed in parallel.
+#
+# * Debug mode
+#
+# The following parameters are available for debugging:
+#
+# test_uapi_headers.py \
+# [-h] [--kheaders KHEADERS] [--vmlinuxh VMLINUXH] [--test TEST]
+#
+# options:
+# -h, --help show this help message and exit
+# --kheaders KHEADERS path to exported kernel headers
+# --vmlinuxh VMLINUXH path to vmlinux.h
+# --test TEST name of the header -or-
+# file with header names -or-
+# special value '*'
+#
+# When --kheaders is specified the temporary directory is not created
+# and KHEADERS is used instead. It is assumed that headers are already
+# installed to KHEADERS.
+#
+# When TEST names a header (e.g. 'linux/tcp.h') it is the to test.
+# When TEST names a file this file should contain a list of
+# headers to test one per line.
+# When TEST is '*' all exported headers are tested.
+#
+# The simplest way to debug an issue with a single header is:
+#
+# ./test_uapi_headers.py --test linux/tcp.h
+
+import subprocess
+import concurrent.futures
+import pathlib
+import time
+import os
+import sys
+import argparse
+import tempfile
+import shutil
+import atexit
+from dataclasses import dataclass
+
+@dataclass
+class Result:
+ header: pathlib.Path
+ returncode: int
+ stderr: str
+
+def run_one(header, kheaders, vmlinuxh):
+ code=f'''
+#include <{header}>
+#include "{vmlinuxh}"
+
+__attribute__((section("tc"), used))
+int syncookie_tc(struct __sk_buff *skb)
+{{
+ return 0;
+}}
+ '''
+ command = f'''
+{os.getenv('CLANG', 'clang')} \
+ -g -Werror -mlittle-endian \
+ -D__x86_64__ \
+ -Xclang -fwchar-type=short \
+ -Xclang -fno-signed-wchar \
+ -I{kheaders}/include/ \
+ -Wno-compare-distinct-pointer-types \
+ -mcpu=v3 \
+ -O2 \
+ -target bpf \
+ -x c \
+ -o /dev/null \
+ -fsyntax-only \
+ -
+'''
+ proc = subprocess.run(command, input=code, capture_output=True,
+ shell=True, encoding='utf8')
+ return Result(header=header,
+ returncode=proc.returncode,
+ stderr=proc.stderr)
+
+def run_all(headers, kheaders, vmlinuxh):
+ start_time = time.time()
+ ok = 0
+ fail = 0
+ failures = []
+ remain = len(headers)
+ print_progress = sys.stdout.isatty()
+ print(f'Processing {remain} headers.')
+ with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
+ for result in executor.map(lambda header: run_one(header, kheaders, vmlinuxh),
+ headers):
+ if result.returncode == 0:
+ print(f"{result.header:<60} ok")
+ ok += 1
+ else:
+ print(f"{result.header:<60} fail")
+ fail += 1
+ failures.append(result)
+ remain -= 1
+ if print_progress:
+ elapsed = time.time() - start_time
+ processed = ok + fail
+ time_per_header = elapsed / processed
+ eta = int(remain * time_per_header)
+ # keep this shorter than header ok/fail line
+ line = f"Ok {ok: >4} Fail {fail: >4} Remain {remain: >4} ETA {eta: >4}s"
+ print(line, end="\r")
+ if print_progress:
+ print('')
+ elapsed = int(time.time() - start_time)
+ if fail == 0:
+ print(f"Done in {elapsed}s, all {len(headers)} ok.")
+ else:
+ print('----- Failure details -----')
+ for result in failures:
+ print(f'{result.header}: rc = {result.returncode}')
+ for line in result.stderr.split('\n'):
+ print(f"{result.header}: {line}")
+ print(f"Done in {elapsed}s, {fail} out of {len(headers)} failed.")
+ return fail == 0
+
+def main(argv):
+ bpf_test_dir = pathlib.Path(__file__).resolve().parent
+ default_vmlinuxh = bpf_test_dir / './tools/include/vmlinux.h'
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--kheaders", type=str, help='path to exported kernel headers')
+ parser.add_argument("--vmlinuxh", type=str, default=default_vmlinuxh,
+ help='path to vmlinux.h')
+ parser.add_argument("--test", type=str,
+ default='./good_uapi_headers.txt',
+ help="name of the header | file with header names | special value '*'")
+ args = parser.parse_args(argv)
+
+ if args.kheaders is None:
+ kheaders = tempfile.mkdtemp(prefix='kheaders')
+ atexit.register(lambda: shutil.rmtree(kheaders))
+ kernel_dir = bpf_test_dir / '../../../../'
+ # Capture both stdout and stderr as stdout to simplify CI logging
+ subprocess.run(f'make -C {kernel_dir} INSTALL_HDR_PATH={kheaders} headers_install',
+ stdout=sys.stdout, stderr=sys.stdout,
+ check=True, shell=True)
+ else:
+ kheaders = args.kheaders
+
+ if os.path.exists(args.test):
+ with open(args.test, 'r') as list_file:
+ headers = [line.strip() for line in list_file]
+ elif args.test == '*':
+ headers = [p.relative_to(f'{kheaders}/include').as_posix()
+ for p in pathlib.Path(kheaders).rglob("*.h")]
+ else:
+ headers = [args.test]
+
+ if run_all(headers, kheaders, args.vmlinuxh):
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
--
2.34.1
next prev parent reply other threads:[~2022-10-25 22:28 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-25 22:27 [RFC bpf-next 00/12] Use uapi kernel headers with vmlinux.h Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 01/12] libbpf: Deduplicate unambigous standalone forward declarations Eduard Zingerman
2022-10-27 22:07 ` Andrii Nakryiko
2022-10-31 1:00 ` Eduard Zingerman
2022-10-31 15:49 ` Eduard Zingerman
2022-11-01 17:08 ` Alan Maguire
2022-11-01 17:37 ` Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 02/12] selftests/bpf: Tests for standalone forward BTF declarations deduplication Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 03/12] libbpf: Support for BTF_DECL_TAG dump in C format Eduard Zingerman
2022-10-27 22:36 ` Andrii Nakryiko
2022-10-25 22:27 ` [RFC bpf-next 04/12] selftests/bpf: Tests " Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 05/12] libbpf: Header guards for selected data structures in vmlinux.h Eduard Zingerman
2022-10-27 22:44 ` Andrii Nakryiko
2022-10-25 22:27 ` [RFC bpf-next 06/12] selftests/bpf: Tests for header guards printing in BTF dump Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 07/12] bpftool: Enable header guards generation Eduard Zingerman
2022-10-25 22:27 ` [RFC bpf-next 08/12] kbuild: Script to infer header guard values for uapi headers Eduard Zingerman
2022-10-27 22:51 ` Andrii Nakryiko
2022-10-25 22:27 ` [RFC bpf-next 09/12] kbuild: Header guards for types from include/uapi/*.h in kernel BTF Eduard Zingerman
2022-10-27 18:43 ` Yonghong Song
2022-10-27 18:55 ` Yonghong Song
2022-10-27 22:44 ` Yonghong Song
2022-10-28 0:00 ` Eduard Zingerman
2022-10-28 0:14 ` Mykola Lysenko
2022-10-28 1:23 ` Yonghong Song
2022-10-28 1:21 ` Yonghong Song
2022-10-25 22:27 ` Eduard Zingerman [this message]
2022-10-25 22:28 ` [RFC bpf-next 11/12] selftests/bpf: Known good uapi headers for test_uapi_headers.py Eduard Zingerman
2022-10-25 22:28 ` [RFC bpf-next 12/12] selftests/bpf: script for infer_header_guards.pl testing Eduard Zingerman
2022-10-25 23:46 ` [RFC bpf-next 00/12] Use uapi kernel headers with vmlinux.h Alexei Starovoitov
2022-10-26 22:46 ` Eduard Zingerman
2022-10-26 11:10 ` Alan Maguire
2022-10-26 23:54 ` Eduard Zingerman
2022-10-27 23:14 ` Andrii Nakryiko
2022-10-28 1:33 ` Yonghong Song
2022-10-28 17:13 ` Andrii Nakryiko
2022-10-28 18:56 ` Yonghong Song
2022-10-28 21:35 ` Andrii Nakryiko
2022-11-01 16:01 ` Alan Maguire
2022-11-01 18:35 ` Alexei Starovoitov
2022-11-01 19:21 ` Eduard Zingerman
2022-11-01 19:44 ` Alexei Starovoitov
2022-11-11 21:55 ` Eduard Zingerman
2022-11-14 7:52 ` Yonghong Song
2022-11-14 21:13 ` Eduard Zingerman
2022-11-14 21:50 ` Alexei Starovoitov
2022-11-16 2:01 ` Eduard Zingerman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221025222802.2295103-11-eddyz87@gmail.com \
--to=eddyz87@gmail.com \
--cc=andrii@kernel.org \
--cc=arnaldo.melo@gmail.com \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=yhs@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox