1use std::ffi::CStr;
3use std::os::unix::fs::DirEntryExt;
4use std::path::{Path, PathBuf};
5use std::{collections::HashMap, fs, process};
6
7use anyhow::{anyhow, Context, Result};
8use libbpf_rs::{libbpf_sys, skel::Skel, MapCore, MapType};
9
10use super::{BPFState, MapState, PinState};
11
12fn read_map(map: &libbpf_rs::Map) -> Result<HashMap<Vec<u8>, Vec<Vec<u8>>>> {
14 let mut data = HashMap::new();
15 if map.map_type().is_percpu() {
16 for key in map.keys() {
17 let val = map.lookup_percpu(key.as_slice(), libbpf_rs::MapFlags::ANY);
18 data.insert(key, val?.context("Map value empty")?);
19 }
20 } else {
21 for key in map.keys() {
22 let val = map.lookup(key.as_slice(), libbpf_rs::MapFlags::ANY);
23 data.insert(key, vec![val?.context("Map value empty")?]);
24 }
25 }
26 Ok(data)
27}
28
29fn map_type_to_libbpf_str(map_type: MapType) -> Result<String> {
32 let c_str = unsafe {
35 let raw_str = libbpf_sys::libbpf_bpf_map_type_str(map_type.into()) as *mut i8;
36 CStr::from_ptr(raw_str)
37 };
38 Ok(c_str.to_str()?.to_string())
39}
40
41fn rust_maps(skel: &dyn Skel, ground_truth: &BPFState) -> Result<HashMap<String, MapState>> {
43 let mut map_states = HashMap::new();
44 for map in skel.object().maps() {
45 let info = map.info()?;
46 let name = info.name()?.to_string();
47 let gt_map_state = match ground_truth.maps.get(&name) {
48 Some(map_state) => map_state,
49 None => return Err(anyhow!("\"{}\" map not found in ground truth", name)),
50 };
51 let data = if gt_map_state.is_static {
53 read_map(&map)?
54 } else {
55 Default::default()
56 };
57 map_states.insert(
58 name,
59 MapState {
60 id: info.info.id,
61 _type: map_type_to_libbpf_str(map.map_type())?,
62 is_static: gt_map_state.is_static,
63 bytes_key: info.info.key_size,
64 bytes_value: info.info.value_size,
65 data,
66 },
67 );
68 }
69 Ok(map_states)
70}
71
72fn rust_pins(pin_dir: &PathBuf) -> Result<PinState> {
74 if !Path::new(pin_dir).exists() {
75 return Err(anyhow!(
76 "pin directory {} not found",
77 pin_dir.to_string_lossy()
78 ));
79 }
80 let mut pins = HashMap::new();
81 for entry in fs::read_dir(pin_dir)? {
82 let entry = entry?;
83 pins.insert(entry.file_name().to_string_lossy().to_string(), entry.ino());
84 }
85 Ok(PinState {
86 dir: pin_dir.to_string_lossy().to_string(),
87 pins,
88 })
89}
90
91pub fn rust_state(skel: &dyn Skel, pin_dir: &PathBuf, ground_truth: &BPFState) -> Result<BPFState> {
94 Ok(BPFState {
95 pid: process::id(),
96 maps: rust_maps(skel, ground_truth)?,
97 pins: rust_pins(pin_dir)?,
98 })
99}