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 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.