1use std::{any::Any, sync::Arc};
39
40use libbpf_rs::{Map, MapType, Object};
41use log::{debug, warn};
42
43use self::{handle::PollingHandle, poller::Poller, preload::attach::AttachLink};
44use crate::{
45 btf_container::BtfContainer,
46 export_event::{
47 type_descriptor::TypeDescriptor, EventExporter, EventExporterBuilder, EventHandler,
48 ExportFormatType,
49 },
50 meta::{EunomiaObjectMeta, MapExportConfig, MapMeta, MapSampleMeta, RunnerConfig},
51 program_poll_loop,
52};
53use anyhow::{anyhow, bail, Context, Result};
54
55const VMLINUX_BTF_PATH: &str = "/sys/kernel/btf/vmlinux";
56const BTF_PATH_ENV_NAME: &str = "BTF_FILE_PATH";
57
58pub mod builder;
60pub mod handle;
62pub(crate) mod poller;
63pub mod preload;
65
66#[cfg(test)]
67#[cfg(not(feature = "no-load-bpf-tests"))]
68mod tests;
69pub struct BpfSkeleton {
71 pub(crate) handle: PollingHandle,
72 pub(crate) meta: EunomiaObjectMeta,
76 #[allow(unused)]
81 pub(crate) config_data: RunnerConfig,
82
83 pub(crate) btf: Arc<BtfContainer>,
86 #[allow(unused)]
88 pub(crate) links: Vec<AttachLink>,
89 pub(crate) prog: Object,
90}
91
92impl BpfSkeleton {
93 pub fn create_poll_handle(&self) -> PollingHandle {
96 self.handle.clone()
97 }
98 pub fn get_program_name(&self) -> &str {
100 &self.meta.bpf_skel.obj_name
101 }
102 pub fn get_map_fd(&self, name: impl AsRef<str>) -> Option<i32> {
105 self.prog.map(name).map(|m| m.fd())
106 }
107 pub fn get_prog_fd(&self, name: impl AsRef<str>) -> Option<i32> {
110 self.prog.prog(name).map(|p| p.fd())
111 }
112
113 fn build_poller_from_exporter<'a>(
114 &self,
115 exporter: Arc<EventExporter>,
116 export_type: ExportMapType<'a>,
117 bpf_map: &'a Map,
118 ) -> Result<Poller<'a>> {
119 let ret = match export_type {
120 ExportMapType::RingBuffer => Poller::RingBuf(
121 self.build_ringbuf_poller(bpf_map, exporter)
122 .with_context(|| anyhow!("Failed to build ringbuf poller"))?,
123 ),
124 ExportMapType::PerfEventArray => Poller::PerfEvent(
125 self.build_perfevent_poller(bpf_map, exporter)
126 .with_context(|| anyhow!("Failed to builf perfevent poller"))?,
127 ),
128 ExportMapType::Sample(sp) => Poller::SampleMap(
129 self.build_sample_map_poller(bpf_map, exporter, sp)
130 .with_context(|| anyhow!("Failed to build sample map poller"))?,
131 ),
132 };
133 Ok(ret)
134 }
135
136 fn wait_and_poll_with_old_single_export(
137 &self,
138 export_format_type: ExportFormatType,
139 export_event_handler: Option<Arc<dyn EventHandler>>,
140 user_context: Option<Arc<dyn Any>>,
141 ) -> Result<()> {
142 let mut export_map: Option<(&MapMeta, ExportMapType)> = None;
143 for map_meta in self.meta.bpf_skel.maps.iter() {
144 let bpf_map = self
145 .prog
146 .map(&map_meta.name)
147 .ok_or_else(|| anyhow!("Map `{}` not found in bpf program", map_meta.name))?;
148 if let Some(sample_meta) = &map_meta.sample {
149 set_and_warn_existsing_map(
150 &mut export_map,
151 map_meta,
152 ExportMapType::Sample(sample_meta),
153 );
154 } else if let MapType::RingBuf = bpf_map.map_type() {
155 set_and_warn_existsing_map(&mut export_map, map_meta, ExportMapType::RingBuffer);
156 } else if let MapType::PerfEventArray = bpf_map.map_type() {
157 set_and_warn_existsing_map(
158 &mut export_map,
159 map_meta,
160 ExportMapType::PerfEventArray,
161 );
162 }
163 }
164 if let Some((map_meta, export_type)) = export_map {
165 let bpf_map = self
166 .prog
167 .map(&map_meta.name)
168 .ok_or_else(|| anyhow!("Invalid map name: {}", map_meta.name))?;
169 let exporter_builder =
170 create_exporter_builder(export_format_type, export_event_handler, user_context);
171 if self.meta.export_types.is_empty() {
172 bail!(
173 "Export map named `{}` found, but no export type is provided",
174 map_meta.name
175 );
176 }
177 let exporter = match export_type {
178 ExportMapType::RingBuffer => exporter_builder.build_for_single_value(
179 &self.meta.export_types[0],
180 self.btf.clone(),
181 &map_meta.intepreter,
182 )?,
183 ExportMapType::PerfEventArray => exporter_builder.build_for_single_value(
184 &self.meta.export_types[0],
185 self.btf.clone(),
186 &map_meta.intepreter,
187 )?,
188 ExportMapType::Sample(sp) => {
189 let map_info = bpf_map.info().with_context(|| {
190 anyhow!("Failed to get map info for `{}`", bpf_map.name())
191 })?;
192 exporter_builder.build_for_key_value(
193 map_info.info.btf_key_type_id,
194 map_info.info.btf_value_type_id,
195 sp,
196 &self.meta.export_types[0],
197 self.btf.clone(),
198 )?
199 }
200 };
201 let poller = self.build_poller_from_exporter(exporter, export_type, bpf_map)?;
202 self.handle.reset();
203 program_poll_loop!(&self.handle, {
204 poller.poll()?;
205 });
206 } else {
207 self.wait_for_no_export_program()
208 .with_context(|| anyhow!("Failed to wait for program"))?;
209 }
210 Ok(())
211 }
212 pub fn wait_and_poll_to_handler_with_multiple_exporter(
215 &self,
216 exporter_provider: impl Fn(
217 &str,
218 ) -> Option<(
219 ExportFormatType,
220 Arc<dyn EventHandler>,
221 Option<Arc<dyn Any>>,
222 )>,
223 ) -> Result<()> {
224 if !self.meta.enable_multiple_export_types {
225 bail!("This function only supports multiple export types");
226 }
227 let mut export_maps: Vec<(&MapMeta, ExportMapType)> = vec![];
228 for map_meta in self
229 .meta
230 .bpf_skel
231 .maps
232 .iter()
233 .filter(|v| !matches!(v.export_config, MapExportConfig::NoExport))
234 {
235 let bpf_map = self
236 .prog
237 .map(&map_meta.name)
238 .ok_or_else(|| anyhow!("Map `{}` not found in bpf program", map_meta.name))?;
239 if let Some(sample_meta) = &map_meta.sample {
240 export_maps.push((map_meta, ExportMapType::Sample(sample_meta)))
241 } else {
242 match bpf_map.map_type() {
243 MapType::RingBuf => export_maps.push((map_meta, ExportMapType::RingBuffer)),
244 MapType::PerfEventArray => {
245 export_maps.push((map_meta, ExportMapType::PerfEventArray))
246 }
247 _ => {
248 debug!(
249 "Ignore map named {}, it's neither ringbuf nor perf event",
250 map_meta.name
251 )
252 }
253 }
254 }
255 }
256 debug!("Export maps: {:#?}", export_maps);
257
258 self.handle.reset();
260 if export_maps.is_empty() {
261 self.wait_for_no_export_program()
262 .with_context(|| anyhow!("Failed to wait for a non-export program"))?;
263 } else {
264 let mut pollers = vec![];
265 for (map_meta, export_map_type) in export_maps.into_iter() {
266 let bpf_map = self
267 .prog
268 .map(&map_meta.name)
269 .ok_or_else(|| anyhow!("Invalid map name: {}", map_meta.name))?;
270 let map_info = bpf_map
271 .info()
272 .with_context(|| anyhow!("Failed to get map info for `{}`", bpf_map.name()))?;
273 let is_sample_map = matches!(export_map_type, ExportMapType::Sample(_));
274 let type_desc = match &map_meta.export_config {
276 MapExportConfig::ExportUseBtf(ty_id) => {
277 TypeDescriptor::BtfType { type_id: *ty_id }
278 }
279 MapExportConfig::ExportUseCustomMembers(mems) => {
280 TypeDescriptor::ManuallyOverride(mems.clone())
281 }
282 MapExportConfig::Default => {
283 if is_sample_map {
284 TypeDescriptor::BtfType {
285 type_id: map_info.info.btf_value_type_id,
286 }
287 } else {
288 bail!("MapExportConfig::Default only applies to sample map");
289 }
290 }
291 MapExportConfig::NoExport => unreachable!("How could you reach here?"),
292 };
293 let builder = EventExporterBuilder::new();
294 let builder = if let Some((ty, handler, ctx)) = exporter_provider(&map_meta.name) {
295 builder
296 .set_export_format(ty)
297 .set_export_event_handler(handler)
298 .set_user_context(ctx)
299 } else {
300 builder
301 };
302 match export_map_type {
303 ExportMapType::RingBuffer => {
304 let exporter = builder
305 .build_for_single_value_with_type_descriptor(
306 type_desc,
307 self.btf.clone(),
308 &map_meta.intepreter,
309 )
310 .with_context(|| anyhow!("Failed to build ringbuf exporter"))?;
311 pollers.push(Poller::RingBuf(
312 self.build_ringbuf_poller(bpf_map, exporter)?,
313 ));
314 }
315 ExportMapType::PerfEventArray => {
316 let exporter = builder
317 .build_for_single_value_with_type_descriptor(
318 type_desc,
319 self.btf.clone(),
320 &map_meta.intepreter,
321 )
322 .with_context(|| anyhow!("Failed to build perf event exporter"))?;
323 pollers.push(Poller::PerfEvent(
324 self.build_perfevent_poller(bpf_map, exporter)?,
325 ));
326 }
327 ExportMapType::Sample(cfg) => {
328 let exporter = builder
329 .build_for_key_value_with_type_desc(
330 TypeDescriptor::BtfType {
331 type_id: map_info.info.btf_key_type_id,
332 },
333 type_desc,
334 cfg,
335 self.btf.clone(),
336 )
337 .with_context(|| {
338 anyhow!(
339 "Failed to build sampling exporter for `{}`",
340 bpf_map.name()
341 )
342 })?;
343 pollers.push(Poller::SampleMap(
344 self.build_sample_map_poller(bpf_map, exporter, cfg)?,
345 ));
346 }
347 }
348 }
349 program_poll_loop!(&self.handle, {
350 for poller in pollers.iter() {
351 poller.poll()?;
352 }
353 });
354 }
355 Ok(())
356 }
357 pub fn wait_and_poll_to_handler(
363 &self,
364 export_format_type: ExportFormatType,
365 export_event_handler: Option<Arc<dyn EventHandler>>,
366 user_context: Option<Arc<dyn Any>>,
367 ) -> Result<()> {
368 if !self.meta.enable_multiple_export_types {
369 return self.wait_and_poll_with_old_single_export(
370 export_format_type,
371 export_event_handler,
372 user_context,
373 );
374 }
375 self.wait_and_poll_to_handler_with_multiple_exporter(|_| {
376 export_event_handler
377 .clone()
378 .map(|v| (export_format_type, v, user_context.clone()))
379 })
380 }
381}
382
383fn set_and_warn_existsing_map<'a>(
384 export_map: &mut Option<(&'a MapMeta, ExportMapType<'a>)>,
385 curr_map: &'a MapMeta,
386 ty: ExportMapType<'a>,
387) {
388 if let Some((meta, _)) = export_map {
389 warn!(
390 "Multiple export maps found, one is `{}`, another is `{}`",
391 meta.name, curr_map.name
392 );
393 }
394 export_map.replace((curr_map, ty));
395}
396#[derive(Debug)]
397enum ExportMapType<'a> {
398 RingBuffer,
399 PerfEventArray,
400 Sample(&'a MapSampleMeta),
401}
402
403fn create_exporter_builder(
404 export_format: ExportFormatType,
405 event_handler: Option<Arc<dyn EventHandler>>,
406 ctx: Option<Arc<dyn Any>>,
407) -> EventExporterBuilder {
408 let exporter_builder = EventExporterBuilder::new().set_export_format(export_format);
409 let exporter_builder = if let Some(hdl) = event_handler.clone() {
410 exporter_builder.set_export_event_handler(hdl)
411 } else {
412 exporter_builder
413 };
414
415 if let Some(user_ctx) = ctx.clone() {
416 exporter_builder.set_user_context(user_ctx)
417 } else {
418 exporter_builder
419 }
420}