1use std::{
8 collections::HashMap,
9 ffi::{c_void, CStr},
10 os::{
11 raw::c_char,
12 unix::prelude::{OsStrExt, PermissionsExt},
13 },
14 path::PathBuf,
15 ptr::NonNull,
16};
17
18use crate::{
19 btf_container::BtfContainer,
20 elf_container::ElfContainer,
21 helper::btf::create_elf_with_btf_section,
22 meta::{ComposedObject, EunomiaObjectMeta, RunnerConfig},
23 skeleton::{BTF_PATH_ENV_NAME, VMLINUX_BTF_PATH},
24};
25use anyhow::{anyhow, bail, Result};
26use bpf_compatible_rs::get_current_system_btf_file;
27use libbpf_rs::{
28 libbpf_sys::{
29 self, bpf_map__name, bpf_map__value_size, bpf_object__btf, bpf_object__next_map,
30 btf__get_raw_data,
31 },
32 ObjectBuilder, OpenObject,
33};
34
35use super::preload::PreLoadBpfSkeleton;
36
37pub struct BpfSkeletonBuilder<'a> {
39 btf_archive_path: Option<&'a str>,
40 object_meta: &'a EunomiaObjectMeta,
41 bpf_object: &'a [u8],
42 runner_config: Option<RunnerConfig>,
43}
44
45impl<'a> BpfSkeletonBuilder<'a> {
46 pub fn from_object_meta_and_object_buffer(
49 meta: &'a EunomiaObjectMeta,
50 bpf_object: &'a [u8],
51 btf_archive_path: Option<&'a str>,
52 ) -> Self {
53 Self {
54 btf_archive_path,
55 object_meta: meta,
56 bpf_object,
57 runner_config: None,
58 }
59 }
60 pub fn from_json_package(
63 package: &'a ComposedObject,
64 btf_archive_path: Option<&'a str>,
65 ) -> Self {
66 Self::from_object_meta_and_object_buffer(
67 &package.meta,
68 &package.bpf_object,
69 btf_archive_path,
70 )
71 }
72 pub fn set_runner_config(self, cfg: RunnerConfig) -> Self {
74 Self {
75 runner_config: Some(cfg),
76 ..self
77 }
78 }
79 pub fn build(self) -> Result<PreLoadBpfSkeleton> {
81 let mut open_bpts = ObjectBuilder::default()
82 .opts(self.object_meta.bpf_skel.obj_name.as_bytes().as_ptr() as *const c_char);
83 let path_holder = if let Some(base_path) = self.btf_archive_path.as_ref() {
85 let path = get_current_system_btf_file(PathBuf::from(base_path).as_path())?;
86 if !path.exists() {
87 bail!("BTF file not found for current system: {}", path.display());
88 }
89 Some(path)
90 } else {
91 None
92 };
93 let env_btf_file_path = std::env::var_os(BTF_PATH_ENV_NAME);
95
96 let vmlinux_btf_exists = if PathBuf::from(VMLINUX_BTF_PATH).exists() {
97 match std::fs::metadata(VMLINUX_BTF_PATH) {
98 Ok(meta) => {
99 meta.permissions().mode() & 0o0400 != 0
101 }
102 Err(e) => {
103 log::info!("Failed to get metadata of {}: {}", VMLINUX_BTF_PATH, e);
104 false
105 }
106 }
107 } else {
108 false
109 };
110 if path_holder.is_some() && !vmlinux_btf_exists {
111 open_bpts.btf_custom_path = path_holder
115 .as_ref()
116 .unwrap()
117 .as_os_str()
118 .as_bytes()
119 .as_ptr() as *const _;
120 } else if let Some(env_btf) = env_btf_file_path.as_ref() {
121 open_bpts.btf_custom_path = env_btf.as_bytes().as_ptr() as *const _;
123 } else if !vmlinux_btf_exists {
124 bail!("All ways tried to find vmlinux BTF, but not found. Please provide the vmlinux btf using env `BTF_FILE_PATH`. (Tried parameter `btf_archive_path`, {}, and {})",BTF_PATH_ENV_NAME,VMLINUX_BTF_PATH);
125 };
126 let open_result = unsafe {
128 libbpf_sys::bpf_object__open_mem(
129 self.bpf_object.as_ptr() as *const c_void,
130 self.bpf_object.len() as libbpf_sys::size_t,
131 &open_bpts,
132 )
133 };
134 if open_result.is_null() {
135 bail!(
136 "Failed to open bpf object: bpf_object__open_mem returned NULL with errno={}",
137 errno::errno()
138 );
139 }
140
141 let btf = {
143 let btf = unsafe { bpf_object__btf(open_result) };
145 if btf.is_null() {
146 bail!("Failed to get btf* from the bpf_object: {}", errno::errno());
147 }
148 let mut dumped_size: u32 = 0;
150 let raw_data = unsafe { btf__get_raw_data(btf, &mut dumped_size as *mut u32) };
152 if raw_data.is_null() {
153 bail!(
154 "Failed to get the raw btf data from btf *: {}",
155 errno::errno()
156 );
157 }
158 let data =
161 unsafe { std::slice::from_raw_parts(raw_data as *const u8, dumped_size as usize) };
162 BtfContainer::new_from_binary(&create_elf_with_btf_section(data, true)?)?
163 };
164
165 let map_value_sizes = {
166 let mut sizes = HashMap::default();
167 let mut curr_map = std::ptr::null();
168 loop {
169 curr_map = unsafe { bpf_object__next_map(open_result, curr_map) };
171 if curr_map.is_null() {
172 break;
173 }
174 let map_name = unsafe { CStr::from_ptr(bpf_map__name(curr_map)) }
176 .to_str()
177 .map_err(|e| anyhow!("Map name contains invalid character: {}", e))?;
178 let value_size = unsafe { bpf_map__value_size(curr_map) };
180 sizes.insert(map_name.into(), value_size);
181 }
182 sizes
183 };
184 let open_object = unsafe { OpenObject::from_ptr(NonNull::new_unchecked(open_result)) }?;
186
187 Ok(PreLoadBpfSkeleton {
188 bpf_object: open_object,
189 config_data: self.runner_config.unwrap_or_default(),
190 btf,
191 meta: self.object_meta.clone(),
192 map_value_sizes,
193 raw_elf: ElfContainer::new_from_binary(self.bpf_object)?,
194 })
195 }
196}
197
198#[cfg(test)]
199#[cfg(not(feature = "no-load-bpf-tests"))]
200mod tests {
201 use libbpf_rs::libbpf_sys::{bpf_map__fd, bpf_map__initial_value, size_t};
202
203 use crate::{
204 meta::ComposedObject, skeleton::builder::BpfSkeletonBuilder, tests::get_assets_dir,
205 };
206
207 #[test]
208 fn test_bpf_skeleton_builder_1() {
209 let package = serde_json::from_str::<ComposedObject>(
210 &std::fs::read_to_string(get_assets_dir().join("runqlat.json")).unwrap(),
211 )
212 .unwrap();
213 let preload = BpfSkeletonBuilder::from_json_package(&package, None)
214 .build()
215 .unwrap();
216 let bpf_object = preload.bpf_object;
218 for map in package.meta.bpf_skel.maps.iter() {
219 let map_from_bpf = bpf_object.map(map.name.as_str()).unwrap();
220 println!("{:?}", map_from_bpf);
221 }
222 for prog in package.meta.bpf_skel.progs.iter() {
224 let prog_from_bpf = bpf_object.prog(prog.name.as_str()).unwrap();
225 println!("{:?}", prog_from_bpf);
226 }
227 {
228 let s = bpf_object.load().unwrap();
229 for map in s.maps_iter() {
230 let mptr = map.as_libbpf_bpf_map_ptr().unwrap();
231 let mut s: size_t = 0;
232 let ptr = unsafe { bpf_map__initial_value(mptr.as_ptr(), &mut s as *mut _) };
233 println!(
234 "{} key size {} value size {} max ent {} mmapedptr {:?} mmaped size {} fd {}",
235 map.name(),
236 map.key_size(),
237 map.value_size(),
238 map.info().unwrap().info.max_entries,
239 ptr,
240 s,
241 unsafe { bpf_map__fd(mptr.as_ptr()) }
242 );
243 }
244 }
245 }
246}