From: Ihor Solodrai <ihor.solodrai@linux.dev>
To: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Quentin Monnet <qmo@kernel.org>
Cc: bpf@vger.kernel.org
Subject: [PATCH bpf-next v2 3/6] selftests/bpf: Compare btf_dump expected output in-process
Date: Fri, 28 Aug 2026 14:52:04 -0700 [thread overview]
Message-ID: <20260828215207.3105313-4-ihor.solodrai@linux.dev> (raw)
In-Reply-To: <20260828215207.3105313-1-ihor.solodrai@linux.dev>
test_btf_dump_case() writes the dumped types to a temporary file and
then runs a command against the expectation:
system("awk '...' 'progs/x.c' | diff -u - '/tmp/x.output.XXXXXX'");
This creates three processes per test case: a shell, awk and diff. The
awk cannot be deferred to the failure path, because its output is the
expected text.
In a VM with a 9p root (used on BPF CI) process creation dominates the
cost. A bare fork+exec measures around 110ms, so the btf_dump test
cases spend most of their runtime forking.
Replace the awk pipeline with a simple marker parser. Buffer the dump
in memory, and use compare_text_to_expected(), which only runs diff(1)
on mismatch.
Measured with "time ./test_progs -t btf_dump" in the VM:
real 2.751s -> 0.702s, sys 1.901s -> 0.193s
This also drops the temporary file setup.
Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
.../selftests/bpf/prog_tests/btf_dump.c | 112 +++++++++++++-----
1 file changed, 82 insertions(+), 30 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index e9d2d4c26509..fe04a955d46c 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <bpf/btf.h>
+#include "testing_helpers.h"
void btf_dump_printf(void *ctx, const char *fmt, va_list args)
{
@@ -43,12 +44,75 @@ static int btf_dump_all_types(const struct btf *btf, void *ctx)
return err;
}
+/*
+ * Expected output is embedded in the test case source, between
+ * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT markers. A region is either
+ * plain C, where a declaration doubles as its own expectation, or C wrapped in
+ * a block comment, where the rendered form differs from the source. All
+ * regions of a file concatenate into one expectation, compared against one
+ * whole-file dump.
+ *
+ * Returns a malloc'd buffer for the caller to free, or NULL on failure.
+ */
+static char *read_expected_output(const char *path)
+{
+ size_t out_sz = 0, line_cap = 0;
+ char *out = NULL, *line = NULL;
+ bool in_region = false;
+ FILE *f, *out_file;
+
+ f = fopen(path, "r");
+ if (!f)
+ return NULL;
+
+ out_file = open_memstream(&out, &out_sz);
+ if (!out_file) {
+ fclose(f);
+ return NULL;
+ }
+
+ while (getline(&line, &line_cap, f) > 0) {
+ const char *p;
+
+ if (strstr(line, "START-EXPECTED-OUTPUT")) {
+ in_region = true;
+ continue;
+ }
+ if (strstr(line, "END-EXPECTED-OUTPUT"))
+ in_region = false;
+ if (!in_region)
+ continue;
+
+ p = line + strspn(line, " \t");
+
+ /* opening or closing line of a commented out region */
+ if (!strncmp(p, "/*", 2) || !strncmp(p, "*/", 2))
+ continue;
+
+ /*
+ * Only a '*' directly after the indentation is a comment
+ * prefix. Without one the line is taken as it is, leading
+ * whitespace included.
+ */
+ p = *p == '*' ? p + 1 : line;
+
+ fputs(p, out_file);
+ }
+
+ free(line);
+ fclose(f);
+ fclose(out_file);
+ return out;
+}
+
static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
{
- char test_file[256], out_file[256], diff_cmd[1024];
+ char *dump = NULL, *expected = NULL;
struct btf *btf = NULL;
- int err = 0, fd = -1;
- FILE *f = NULL;
+ char test_file[256];
+ size_t dump_sz = 0;
+ int err = 0;
+ FILE *f;
snprintf(test_file, sizeof(test_file), "%s.bpf.o", t->file);
@@ -72,21 +136,14 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
ASSERT_EQ(ptr_sz, (size_t)8, "ptr_sz");
}
- snprintf(out_file, sizeof(out_file), "/tmp/%s.output.XXXXXX", t->file);
- fd = mkstemp(out_file);
- if (!ASSERT_GE(fd, 0, "create_tmp")) {
- err = fd;
- goto done;
- }
- f = fdopen(fd, "w");
- if (!ASSERT_OK_PTR(f, "open_tmp")) {
- close(fd);
+ f = open_memstream(&dump, &dump_sz);
+ if (!ASSERT_OK_PTR(f, "open_memstream")) {
+ err = -errno;
goto done;
}
err = btf_dump_all_types(btf, f);
fclose(f);
- close(fd);
if (!ASSERT_OK(err, "btf_dump"))
goto done;
@@ -97,28 +154,23 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
* without preserving the directory structure.
*/
snprintf(test_file, sizeof(test_file), "%s.c", t->file);
- /*
- * Diff test output and expected test output, contained between
- * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case.
- * For expected output lines, everything before '*' is stripped out.
- * Also lines containing comment start and comment end markers are
- * ignored.
- */
- snprintf(diff_cmd, sizeof(diff_cmd),
- "awk '/START-EXPECTED-OUTPUT/{out=1;next} "
- "/END-EXPECTED-OUTPUT/{out=0} "
- "/\\/\\*|\\*\\//{next} " /* ignore comment start/end lines */
- "out {sub(/^[ \\t]*\\*/, \"\"); print}' '%s' | diff -u - '%s'",
- test_file, out_file);
- err = system(diff_cmd);
- if (!ASSERT_OK(err, "diff")) {
- fprintf(stdout, "output=%s, diff cmd:\n%s\n", out_file, diff_cmd);
+
+ expected = read_expected_output(test_file);
+ if (!ASSERT_OK_PTR(expected, "read_expected_output")) {
+ err = -errno;
goto done;
}
- remove(out_file);
+ /*
+ * The mismatch has already been reported, so this only has to
+ * register the failure. ASSERT_OK() would append a stale errno to it.
+ */
+ err = compare_text_to_expected(dump, expected);
+ ASSERT_EQ(err, 0, "compare_text_to_expected");
done:
+ free(expected);
+ free(dump);
btf__free(btf);
return err;
}
--
2.55.0
next prev parent reply other threads:[~2026-08-28 21:52 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-28 21:52 [PATCH bpf-next v2 0/6] bpftool, selftests: Add tests for C dump and fix a dropped type Ihor Solodrai
2026-08-28 21:52 ` [PATCH bpf-next v2 1/6] selftests/bpf: Add compare_text_to_expected() helper Ihor Solodrai
2026-08-28 21:52 ` [PATCH bpf-next v2 2/6] selftests/bpf: Modernize btf_dump test scaffolding Ihor Solodrai
2026-08-28 21:52 ` Ihor Solodrai [this message]
2026-08-28 21:52 ` [PATCH bpf-next v2 4/6] selftests/bpf: NUL-terminate bpftool command output Ihor Solodrai
2026-08-28 21:52 ` [PATCH bpf-next v2 5/6] selftests/bpf: Add tests for bpftool btf dump format c Ihor Solodrai
2026-08-28 21:52 ` [PATCH bpf-next v2 6/6] bpftool: Don't drop a type in the sorted C dump Ihor Solodrai
2026-08-30 1:30 ` [PATCH bpf-next v2 0/6] bpftool, selftests: Add tests for C dump and fix a dropped type patchwork-bot+netdevbpf
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=20260828215207.3105313-4-ihor.solodrai@linux.dev \
--to=ihor.solodrai@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=memxor@gmail.com \
--cc=qmo@kernel.org \
/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