scx_arena_selftests/
main.rs1mod bpf_skel;
6pub use bpf_skel::*;
7
8use std::mem::MaybeUninit;
9
10use anyhow::bail;
11use anyhow::Context;
12use anyhow::Result;
13
14use std::ffi::c_ulong;
15use std::ffi::c_void;
16
17use std::os::fd::AsFd;
18use std::os::fd::AsRawFd;
19use std::sync::Arc;
20
21use scx_utils::init_libbpf_logging;
22use scx_utils::Core;
23use scx_utils::Llc;
24use scx_utils::Topology;
25use scx_utils::NR_CPU_IDS;
26
27use simplelog::{ColorChoice, Config as SimplelogConfig, TermLogger, TerminalMode};
28
29use libbpf_rs::libbpf_sys;
30
31use libbpf_rs::skel::OpenSkel;
32use libbpf_rs::skel::SkelBuilder;
33use libbpf_rs::PrintLevel;
34use libbpf_rs::ProgramInput;
35
36const BPF_STDOUT: u32 = 1;
37const BPF_STDERR: u32 = 2;
38
39fn setup_arenas(skel: &mut BpfSkel<'_>) -> Result<()> {
40 const STATIC_ALLOC_PAGES_GRANULARITY: c_ulong = 512;
41 const TASK_SIZE: c_ulong = 42;
42
43 let mut args = types::arena_init_args {
47 static_pages: STATIC_ALLOC_PAGES_GRANULARITY,
48 task_ctx_size: TASK_SIZE,
49 };
50
51 let input = ProgramInput {
52 context_in: Some(unsafe {
53 std::slice::from_raw_parts_mut(
54 &mut args as *mut _ as *mut u8,
55 std::mem::size_of_val(&args),
56 )
57 }),
58 ..Default::default()
59 };
60
61 let output = skel.progs.arena_init.test_run(input)?;
62 if output.return_value != 0 {
63 bail!(
64 "Could not initialize arenas, arena_init returned {}",
65 output.return_value as i32
66 );
67 }
68
69 Ok(())
70}
71
72fn setup_topology_node(skel: &mut BpfSkel<'_>, mask: &[u64]) -> Result<()> {
73 let mut args = types::arena_alloc_mask_args {
74 bitmap: 0 as c_ulong,
75 };
76
77 let input = ProgramInput {
78 context_in: Some(unsafe {
79 std::slice::from_raw_parts_mut(
80 &mut args as *mut _ as *mut u8,
81 std::mem::size_of_val(&args),
82 )
83 }),
84 ..Default::default()
85 };
86
87 let output = skel.progs.arena_alloc_mask.test_run(input)?;
88 if output.return_value != 0 {
89 bail!(
90 "Could not initialize arenas, setup_topology_node returned {}",
91 output.return_value as i32
92 );
93 }
94
95 let ptr = unsafe {
96 &mut *std::ptr::with_exposed_provenance_mut::<[u64; 10]>(args.bitmap.try_into().unwrap())
97 };
98
99 let (valid_mask, _) = ptr.split_at_mut(mask.len());
100 valid_mask.clone_from_slice(mask);
101
102 let mut args = types::arena_topology_node_init_args {
103 bitmap: args.bitmap as c_ulong,
104 data_size: 0 as c_ulong,
105 id: 0 as c_ulong,
106 };
107
108 let input = ProgramInput {
109 context_in: Some(unsafe {
110 std::slice::from_raw_parts_mut(
111 &mut args as *mut _ as *mut u8,
112 std::mem::size_of_val(&args),
113 )
114 }),
115 ..Default::default()
116 };
117
118 let output = skel.progs.arena_topology_node_init.test_run(input)?;
119 if output.return_value != 0 {
120 bail!(
121 "arena_topology_node_init returned {}",
122 output.return_value as i32
123 );
124 }
125
126 Ok(())
127}
128
129fn setup_topology(skel: &mut BpfSkel<'_>) -> Result<()> {
130 let topo = Topology::new().expect("Failed to build host topology");
131
132 setup_topology_node(skel, topo.span.as_raw_slice())?;
133
134 for (_, node) in topo.nodes {
135 setup_topology_node(skel, node.span.as_raw_slice())?;
136 }
137
138 for (_, llc) in topo.all_llcs {
139 setup_topology_node(
140 skel,
141 Arc::<Llc>::into_inner(llc)
142 .expect("missing llc")
143 .span
144 .as_raw_slice(),
145 )?;
146 }
147
148 for (_, core) in topo.all_cores {
149 setup_topology_node(
150 skel,
151 Arc::<Core>::into_inner(core)
152 .expect("missing core")
153 .span
154 .as_raw_slice(),
155 )?;
156 }
157 for (_, cpu) in topo.all_cpus {
158 let mut mask = [0; 9];
159 mask[cpu.id.checked_shr(64).unwrap_or(0)] |= 1 << (cpu.id % 64);
160 setup_topology_node(skel, &mask)?;
161 }
162
163 Ok(())
164}
165
166fn print_stream(skel: &mut BpfSkel<'_>, stream_id: u32) -> () {
167 let mut buf = vec![0u8; 4096];
168 let name = if stream_id == 1 { "OUTPUT" } else { "ERROR" };
169 let mut started = false;
170
171 loop {
172 let ret = unsafe {
173 libbpf_sys::bpf_prog_stream_read(
174 skel.progs.arena_selftest.as_fd().as_raw_fd(),
175 stream_id,
176 buf.as_mut_ptr() as *mut c_void,
177 buf.len() as u32,
178 std::ptr::null_mut(),
179 )
180 };
181 if ret < 0 {
182 eprintln!("STREAM {} UNAVAILABLE (REQUIRES >= v6.17)", name);
183 return;
184 }
185
186 if !started {
187 println!("===BEGIN STREAM {}===", name);
188 started = true;
189 }
190
191 if ret == 0 {
192 break;
193 }
194
195 print!("{}", String::from_utf8_lossy(&buf[..ret as usize]));
196 }
197
198 println!("\n====END STREAM {}====", name);
199}
200
201fn main() {
202 TermLogger::init(
203 simplelog::LevelFilter::Info,
204 SimplelogConfig::default(),
205 TerminalMode::Mixed,
206 ColorChoice::Auto,
207 )
208 .unwrap();
209
210 let mut open_object = MaybeUninit::uninit();
211 let mut builder = BpfSkelBuilder::default();
212
213 builder.obj_builder.debug(true);
214 init_libbpf_logging(Some(PrintLevel::Debug));
215
216 let mut skel = builder
217 .open(&mut open_object)
218 .context("Failed to open BPF program")
219 .unwrap();
220
221 skel.maps.rodata_data.as_mut().unwrap().nr_cpu_ids = *NR_CPU_IDS as u32;
222
223 let mut skel = skel.load().context("Failed to load BPF program").unwrap();
224
225 setup_arenas(&mut skel).unwrap();
226 setup_topology(&mut skel).unwrap();
227
228 let input = ProgramInput {
229 ..Default::default()
230 };
231
232 let output = skel.progs.arena_selftest.test_run(input).unwrap();
233 if output.return_value != 0 {
234 eprintln!(
235 "Selftest returned {}, please check bpf tracelog for more details.",
236 output.return_value as i32
237 );
238 } else {
239 println!("Selftest successful.");
240 }
241
242 print_stream(&mut skel, BPF_STDOUT);
243 print_stream(&mut skel, BPF_STDERR);
244}