Skip to content

Commit b0f64d4

Browse files
committed
Add tests for BPF Streams
Add tests for BPF Streams. The tests access both the stdout and stderr streams, and ensure the code works regardless of whether there is ready data. Signed-off-by: Emil Tsalapatis <[email protected]>
1 parent bc159f9 commit b0f64d4

File tree

3 files changed

+141
-1
lines changed

3 files changed

+141
-1
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_helpers.h>
6+
7+
/* Definition of can_loop taken from bpf_experimental.h. */
8+
#ifdef __BPF_FEATURE_MAY_GOTO
9+
#define can_loop \
10+
({ \
11+
__label__ l_break, l_continue; \
12+
bool ret = true; \
13+
asm volatile goto("may_goto %l[l_break]" :: ::l_break); \
14+
goto l_continue; \
15+
l_break: \
16+
ret = false; \
17+
l_continue:; \
18+
ret; \
19+
})
20+
#else
21+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
22+
#define can_loop \
23+
({ \
24+
__label__ l_break, l_continue; \
25+
bool ret = true; \
26+
asm volatile goto("1:.byte 0xe5; \
27+
.byte 0; \
28+
.long ((%l[l_break] - 1b - 8) / 8) & 0xffff; \
29+
.short 0" :: ::l_break); \
30+
goto l_continue; \
31+
l_break: \
32+
ret = false; \
33+
l_continue:; \
34+
ret; \
35+
})
36+
#else
37+
#define can_loop \
38+
({ \
39+
__label__ l_break, l_continue; \
40+
bool ret = true; \
41+
asm volatile goto("1:.byte 0xe5; \
42+
.byte 0; \
43+
.long (((%l[l_break] - 1b - 8) / 8) & 0xffff) << 16; \
44+
.short 0" :: ::l_break); \
45+
goto l_continue; \
46+
l_break: \
47+
ret = false; \
48+
l_continue:; \
49+
ret; \
50+
})
51+
#endif
52+
#endif
53+
54+
volatile u64 i;
55+
56+
/*
57+
* Trigger a may_goto timeout to emit a streams error. As of 6.19 the only way
58+
* to trigger streams output is by causing an error condition in the program.
59+
* One of these is a loop timeout: The may_goto macro allows for loops that
60+
* cannot be verified by embedding a timer that is guaranteed to expire in the
61+
* condition, simplifying verification. When the timer expires, the kernel
62+
* writes an error message to the stderr stream of the BPF program. This is the
63+
* case below.
64+
*/
65+
SEC("syscall")
66+
int trigger_streams(void *ctx)
67+
{
68+
while (i == 0 && can_loop)
69+
;
70+
return 0;
71+
}
72+
73+
char LICENSE[] SEC("license") = "GPL";

libbpf-rs/tests/common/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use libbpf_rs::ObjectBuilder;
1515
use libbpf_rs::OpenObject;
1616
use libbpf_rs::ProgramMut;
1717

18-
1918
pub fn get_test_object_path(filename: &str) -> PathBuf {
2019
let mut path = PathBuf::new();
2120
// env!() macro fails at compile time if var not found

libbpf-rs/tests/test_streams.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Tests for BPF program streams (stdout/stderr).
2+
3+
mod common;
4+
5+
use std::io::Read;
6+
7+
use libbpf_rs::ProgramInput;
8+
use test_tag::tag;
9+
10+
use crate::common::get_prog_mut;
11+
use crate::common::get_test_object;
12+
13+
/// Test that we can read from a BPF program's stdout stream.
14+
///
15+
/// This test loads a BPF program that triggers the streams mechanism,
16+
/// runs it, and then attempts to read from the stdout stream.
17+
#[tag(root)]
18+
#[test]
19+
#[ignore]
20+
fn test_stream_stdout_read() {
21+
let mut obj = get_test_object("stream.bpf.o");
22+
let prog = get_prog_mut(&mut obj, "trigger_streams");
23+
24+
let input = ProgramInput::default();
25+
let _output = prog.test_run(input).unwrap();
26+
27+
let mut stdout = prog.stdout();
28+
let mut buf = [0u8; 1024];
29+
30+
// The read itself should succeed and return 0 bytes
31+
let result = stdout.read(&mut buf);
32+
assert!(
33+
result.is_ok(),
34+
"Failed to read from stdout stream: {:?}",
35+
result.err()
36+
);
37+
38+
let len = result.unwrap();
39+
assert!(
40+
len == 0,
41+
"Unexpectedly read {} characters from stderr stream",
42+
len
43+
);
44+
}
45+
46+
#[tag(root)]
47+
#[test]
48+
#[ignore]
49+
fn test_stream_stderr_read() {
50+
let mut obj = get_test_object("stream.bpf.o");
51+
let prog = get_prog_mut(&mut obj, "trigger_streams");
52+
53+
let input = ProgramInput::default();
54+
let _output = prog.test_run(input).unwrap();
55+
56+
let mut stderr = prog.stderr();
57+
let mut buf = [0u8; 1024];
58+
59+
// The read should successfully read a non-zero amount of bytes
60+
let result = stderr.read(&mut buf);
61+
assert!(
62+
result.is_ok(),
63+
"Failed to read from stderr stream: {:?}",
64+
result.err()
65+
);
66+
67+
assert!(result.unwrap() != 0, "No output from stderr stream");
68+
}

0 commit comments

Comments
 (0)