Skip to content

Commit 3693a75

Browse files
committed
selftests/bpf: Add tests for prog streams
Add selftests to stress test the various facets of the stream API, memory allocation pattern, and ensuring dumping support is tested and functional. Create symlink to bpftool stream.bpf.c and use it to test the support to dump messages to ringbuf in user space, and verify output. Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]>
1 parent f3614cd commit 3693a75

File tree

4 files changed

+313
-0
lines changed

4 files changed

+313
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
#include <test_progs.h>
4+
#include <sys/mman.h>
5+
6+
#include "stream.skel.h"
7+
#include "stream_fail.skel.h"
8+
9+
#include "stream_bpftool.skel.h"
10+
11+
void test_stream_failure(void)
12+
{
13+
RUN_TESTS(stream_fail);
14+
}
15+
16+
void test_stream_success(void)
17+
{
18+
RUN_TESTS(stream);
19+
RUN_TESTS(stream_bpftool);
20+
return;
21+
}
22+
23+
typedef int (*sample_cb_t)(void *, void *, size_t);
24+
25+
static void stream_ringbuf_output(int prog_id, sample_cb_t sample_cb)
26+
{
27+
LIBBPF_OPTS(bpf_test_run_opts, opts);
28+
struct ring_buffer *ringbuf;
29+
struct stream_bpftool *skel;
30+
int fd, ret;
31+
32+
skel = stream_bpftool__open_and_load();
33+
if (!ASSERT_OK_PTR(skel, "stream_bpftool_open_and_load"))
34+
return;
35+
36+
fd = bpf_map__fd(skel->maps.ringbuf);
37+
38+
ringbuf = ring_buffer__new(fd, sample_cb, NULL, NULL);
39+
if (!ASSERT_OK_PTR(ringbuf, "ringbuf_new"))
40+
goto end;
41+
42+
skel->bss->prog_id = prog_id;
43+
skel->bss->stream_id = 1;
44+
do {
45+
skel->bss->written_count = skel->bss->written_size = 0;
46+
ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.bpftool_dump_prog_stream), &opts);
47+
if (ret)
48+
break;
49+
ret = ring_buffer__consume_n(ringbuf, skel->bss->written_count);
50+
if (!ASSERT_EQ(ret, skel->bss->written_count, "consume"))
51+
break;
52+
ret = 0;
53+
} while (opts.retval == EAGAIN);
54+
55+
ASSERT_OK(ret, "ret");
56+
ASSERT_EQ(opts.retval, 0, "retval");
57+
58+
end:
59+
stream_bpftool__destroy(skel);
60+
}
61+
62+
int cnt = 0;
63+
64+
static int process_sample(void *ctx, void *data, size_t len)
65+
{
66+
char buf[64];
67+
68+
snprintf(buf, sizeof(buf), "num=%d\n", cnt++);
69+
ASSERT_TRUE(strcmp(buf, (char *)data) == 0, "sample strcmp");
70+
return 0;
71+
}
72+
73+
void test_stream_output(void)
74+
{
75+
LIBBPF_OPTS(bpf_test_run_opts, opts);
76+
struct bpf_prog_info info = {};
77+
__u32 info_len = sizeof(info);
78+
struct stream *skel;
79+
int ret;
80+
81+
skel = stream__open_and_load();
82+
if (!ASSERT_OK_PTR(skel, "stream__open_and_load"))
83+
return;
84+
85+
ASSERT_OK(bpf_prog_get_info_by_fd(bpf_program__fd(skel->progs.stream_test_output), &info, &info_len), "get info");
86+
ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.stream_test_output), &opts);
87+
ASSERT_OK(ret, "ret");
88+
ASSERT_OK(opts.retval, "retval");
89+
stream_ringbuf_output(info.id, process_sample);
90+
91+
ASSERT_EQ(cnt, 1000, "cnt");
92+
93+
stream__destroy(skel);
94+
return;
95+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include "bpf_misc.h"
7+
8+
#define _STR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
9+
10+
#define STREAM_STR (u64)(_STR _STR _STR _STR)
11+
12+
static __noinline int stream_exercise(int id, int N)
13+
{
14+
struct bpf_stream_elem *elem, *earr[56] = {};
15+
struct bpf_stream *stream;
16+
int ret;
17+
u32 i;
18+
19+
if (N > 56)
20+
return 56;
21+
22+
stream = bpf_stream_get(id, NULL);
23+
if (!stream)
24+
return 1;
25+
for (i = 0; i < N; i++)
26+
if ((ret = bpf_stream_vprintk(stream, "%llu%s", &(u64[]){i, STREAM_STR}, 16)) < 0) {
27+
bpf_printk("bpf_stream_vprintk ret=%d", ret);
28+
return 2;
29+
}
30+
ret = 0;
31+
for (i = 0; i < N; i++) {
32+
elem = bpf_stream_next_elem(stream);
33+
if (!elem) {
34+
ret = 4;
35+
break;
36+
}
37+
earr[i] = elem;
38+
}
39+
elem = bpf_stream_next_elem(stream);
40+
if (elem) {
41+
bpf_stream_free_elem(elem);
42+
ret = 5;
43+
}
44+
for (i = 0; i < N; i++)
45+
if (earr[i])
46+
bpf_stream_free_elem(earr[i]);
47+
return ret;
48+
}
49+
50+
static __noinline int stream_exercise_nums(int id)
51+
{
52+
int ret = 0;
53+
54+
ret = ret ?: stream_exercise(id, 56);
55+
ret = ret ?: stream_exercise(id, 42);
56+
ret = ret ?: stream_exercise(id, 28);
57+
ret = ret ?: stream_exercise(id, 10);
58+
ret = ret ?: stream_exercise(id, 1);
59+
60+
return ret;
61+
}
62+
63+
SEC("syscall")
64+
__success __retval(0)
65+
int stream_test(void *ctx)
66+
{
67+
unsigned long flags;
68+
int ret;
69+
70+
bpf_local_irq_save(&flags);
71+
bpf_repeat(50) {
72+
ret = stream_exercise_nums(BPF_STDOUT);
73+
if (ret)
74+
break;
75+
}
76+
if (ret) {
77+
bpf_local_irq_restore(&flags);
78+
return ret;
79+
}
80+
bpf_repeat(100) {
81+
ret = stream_exercise_nums(BPF_STDERR);
82+
if (ret)
83+
break;
84+
}
85+
bpf_local_irq_restore(&flags);
86+
87+
if (ret)
88+
return ret;
89+
90+
ret = stream_exercise_nums(BPF_STDOUT);
91+
if (ret)
92+
return ret;
93+
return stream_exercise_nums(BPF_STDERR);
94+
}
95+
96+
SEC("syscall")
97+
__success __retval(0)
98+
int stream_test_output(void *ctx)
99+
{
100+
for (int i = 0; i < 1000; i++)
101+
bpf_stream_printk(BPF_STDOUT, "num=%d\n", i);
102+
return 0;
103+
}
104+
105+
SEC("syscall")
106+
__success __retval(0)
107+
int stream_test_limit(void *ctx)
108+
{
109+
struct bpf_stream *stream;
110+
bool failed = false;
111+
112+
stream = bpf_stream_get(BPF_STDOUT, NULL);
113+
if (!stream)
114+
return 2;
115+
116+
bpf_repeat(BPF_MAX_LOOPS) {
117+
failed = bpf_stream_vprintk(stream, "%s%s%s", &(u64[]){STREAM_STR, STREAM_STR}, 16) != 0;
118+
if (failed)
119+
break;
120+
}
121+
122+
if (failed)
123+
return 0;
124+
return 1;
125+
}
126+
127+
char _license[] SEC("license") = "GPL";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../bpf/bpftool/skeleton/stream.bpf.c
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
#include <vmlinux.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_core_read.h>
7+
#include "bpf_misc.h"
8+
9+
SEC("syscall")
10+
__failure __msg("R1 type=trusted_ptr_or_null_ expected=")
11+
int stream_get_trusted(void *ctx) {
12+
struct bpf_stream *stream;
13+
14+
stream = bpf_stream_get(BPF_STDOUT, NULL);
15+
bpf_this_cpu_ptr(stream);
16+
return 0;
17+
}
18+
19+
SEC("tc")
20+
__failure __msg("calling kernel function bpf_prog_stream_get is not allowed")
21+
int stream_get_prog_fail(void *ctx) {
22+
struct bpf_stream *stream;
23+
24+
stream = bpf_prog_stream_get(BPF_STDOUT, 0);
25+
if (!stream)
26+
return 0;
27+
bpf_this_cpu_ptr(stream);
28+
return 0;
29+
}
30+
31+
SEC("syscall")
32+
__failure __msg("R1 type=ptr_or_null_ expected=")
33+
int stream_get_prog_trusted(void *ctx) {
34+
struct bpf_stream *stream;
35+
36+
stream = bpf_prog_stream_get(BPF_STDOUT, 0);
37+
bpf_this_cpu_ptr(stream);
38+
return 0;
39+
}
40+
41+
SEC("syscall")
42+
__failure __msg("Unreleased reference")
43+
int stream_get_put_missing(void *ctx) {
44+
struct bpf_stream *stream;
45+
46+
stream = bpf_prog_stream_get(BPF_STDOUT, 0);
47+
if (!stream)
48+
return 0;
49+
return 0;
50+
}
51+
52+
SEC("syscall")
53+
__failure __msg("R1 must be referenced or trusted")
54+
int stream_next_untrusted_arg(void *ctx)
55+
{
56+
struct bpf_stream *stream;
57+
58+
stream = bpf_core_cast((void *)0xdeadbeef, typeof(*stream));
59+
bpf_stream_next_elem(stream);
60+
return 0;
61+
}
62+
63+
SEC("syscall")
64+
__failure __msg("Possibly NULL pointer passed")
65+
int stream_next_null_arg(void *ctx)
66+
{
67+
bpf_stream_next_elem(NULL);
68+
return 0;
69+
}
70+
71+
SEC("syscall")
72+
__failure __msg("R1 must be referenced or trusted")
73+
int stream_vprintk_untrusted_arg(void *ctx)
74+
{
75+
struct bpf_stream *stream;
76+
77+
stream = bpf_core_cast((void *)0xfaceb00c, typeof(*stream));
78+
bpf_stream_vprintk(stream, "", NULL, 0);
79+
return 0;
80+
}
81+
82+
SEC("syscall")
83+
__failure __msg("Possibly NULL pointer passed")
84+
int stream_vprintk_null_arg(void *ctx)
85+
{
86+
bpf_stream_vprintk(NULL, "", NULL, 0);
87+
return 0;
88+
}
89+
90+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)