Skip to content

Commit e483371

Browse files
d-e-s-odanielocfb
authored andcommitted
Add CHANGELOG entry for #269
This change addresses a few remaining issues of #269 and adds a CHANGELOG entry. Signed-off-by: Daniel Müller <[email protected]>
1 parent d9409ea commit e483371

File tree

4 files changed

+92
-43
lines changed

4 files changed

+92
-43
lines changed

examples/bpf_query/src/main.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ fn prog(args: ProgArgs) {
2828
for prog in query::ProgInfoIter::with_query_opts(opts) {
2929
println!(
3030
"name={:<16} type={:<15} run_count={:<2} runtime_ns={}",
31-
prog.name, prog.ty, prog.run_cnt, prog.run_time_ns
31+
prog.name.to_string_lossy(),
32+
prog.ty,
33+
prog.run_cnt,
34+
prog.run_time_ns
3235
);
3336
if args.disassemble {
3437
#[cfg(target_arch = "x86_64")]
@@ -56,13 +59,18 @@ fn prog(args: ProgArgs) {
5659

5760
fn map() {
5861
for map in query::MapInfoIter::default() {
59-
println!("name={:<16} type={}", map.name, map.ty);
62+
println!("name={:<16} type={}", map.name.to_string_lossy(), map.ty);
6063
}
6164
}
6265

6366
fn btf() {
6467
for btf in query::BtfInfoIter::default() {
65-
println!("id={:4} name={} size={}", btf.id, btf.name, btf.btf.len());
68+
println!(
69+
"id={:4} name={} size={}",
70+
btf.id,
71+
btf.name.to_string_lossy(),
72+
btf.btf.len()
73+
);
6674
}
6775
}
6876

libbpf-rs/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
Unreleased
2+
----------
3+
- Overhauled `query::ProgramInfo` and `query::ProgInfoIter` to make them more
4+
readily usable
5+
6+
17
0.21.2
28
------
39
- Enabled key iteration on `MapHandle` objects (formerly possible only on `Map`

libbpf-rs/src/query.rs

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
//!
77
//! let mut iter = ProgInfoIter::default();
88
//! for prog in iter {
9-
//! println!("{}", prog.name);
9+
//! println!("{}", prog.name.to_string_lossy());
1010
//! }
1111
//! ```
1212
13-
use core::ffi::c_void;
1413
use std::convert::TryFrom;
14+
use std::ffi::c_void;
15+
use std::ffi::CString;
1516
use std::mem::size_of_val;
1617
use std::os::raw::c_char;
17-
use std::string::String;
1818
use std::time::Duration;
1919

2020
use nix::errno;
@@ -91,19 +91,6 @@ macro_rules! gen_info_impl {
9191
};
9292
}
9393

94-
fn name_arr_to_string(a: &[c_char], default: &str) -> String {
95-
let converted_arr: Vec<u8> = a
96-
.iter()
97-
.take_while(|x| **x != 0)
98-
.map(|x| *x as u8)
99-
.collect();
100-
if !converted_arr.is_empty() {
101-
String::from_utf8(converted_arr).unwrap_or_else(|_| default.to_string())
102-
} else {
103-
default.to_string()
104-
}
105-
}
106-
10794
/// BTF Line information
10895
#[derive(Clone, Debug)]
10996
pub struct LineInfo {
@@ -131,17 +118,17 @@ impl From<&libbpf_sys::bpf_line_info> for LineInfo {
131118
}
132119
}
133120

121+
/// Bpf identifier tag
134122
#[derive(Debug, Clone, Default)]
135123
#[repr(C)]
136-
/// Bpf identifier tag
137124
pub struct Tag([u8; 8]);
138125

139126
/// Information about a BPF program
140127
#[derive(Debug, Clone)]
141128
// TODO: Document members.
142129
#[allow(missing_docs)]
143130
pub struct ProgramInfo {
144-
pub name: String,
131+
pub name: CString,
145132
pub ty: ProgramType,
146133
pub tag: Tag,
147134
pub id: u32,
@@ -169,15 +156,15 @@ pub struct ProgramInfo {
169156
pub run_cnt: u64,
170157
}
171158

172-
#[derive(Default, Debug)]
173159
/// An iterator for the information of loaded bpf programs
160+
#[derive(Default, Debug)]
174161
pub struct ProgInfoIter {
175162
cur_id: u32,
176163
opts: ProgInfoQueryOptions,
177164
}
178165

179-
#[derive(Clone, Default, Debug)]
180166
/// Options to query the program info currently loaded
167+
#[derive(Clone, Default, Debug)]
181168
pub struct ProgInfoQueryOptions {
182169
/// Include the vector of bpf instructions in the result
183170
include_xlated_prog_insns: bool,
@@ -264,17 +251,19 @@ impl ProgInfoQueryOptions {
264251
self
265252
}
266253

267-
/// Include all
254+
/// Include everything there is in the query results
268255
pub fn include_all(self) -> Self {
269-
self.include_xlated_prog_insns(true)
270-
.include_jited_prog_insns(true)
271-
.include_map_ids(true)
272-
.include_line_info(true)
273-
.include_func_info(true)
274-
.include_jited_line_info(true)
275-
.include_jited_func_lens(true)
276-
.include_prog_tags(true)
277-
.include_jited_ksyms(true)
256+
Self {
257+
include_xlated_prog_insns: true,
258+
include_jited_prog_insns: true,
259+
include_map_ids: true,
260+
include_line_info: true,
261+
include_func_info: true,
262+
include_jited_line_info: true,
263+
include_jited_func_lens: true,
264+
include_prog_tags: true,
265+
include_jited_ksyms: true,
266+
}
278267
}
279268
}
280269

@@ -299,7 +288,8 @@ impl ProgramInfo {
299288
unsafe { libbpf_sys::bpf_obj_get_info_by_fd(fd, item_ptr as *mut c_void, &mut len) };
300289
util::parse_ret(ret)?;
301290

302-
let name = name_arr_to_string(&item.name, "(?)");
291+
// SANITY: `libbpf` should guarantee NUL termination.
292+
let name = util::c_char_slice_to_cstr(&item.name).unwrap();
303293
let ty = match ProgramType::try_from(item.type_) {
304294
Ok(ty) => ty,
305295
Err(_) => ProgramType::Unknown,
@@ -379,7 +369,7 @@ impl ProgramInfo {
379369
util::parse_ret(ret)?;
380370

381371
return Ok(ProgramInfo {
382-
name,
372+
name: name.to_owned(),
383373
ty,
384374
tag: Tag(item.tag),
385375
id: item.id,
@@ -430,7 +420,8 @@ impl Iterator for ProgInfoIter {
430420

431421
match prog {
432422
Ok(p) => return Some(p),
433-
Err(e) => eprintln!("Failed to load program: {}", e),
423+
// TODO: We should consider bubbling up errors properly.
424+
Err(_err) => (),
434425
}
435426
}
436427
}
@@ -441,7 +432,7 @@ impl Iterator for ProgInfoIter {
441432
// TODO: Document members.
442433
#[allow(missing_docs)]
443434
pub struct MapInfo {
444-
pub name: String,
435+
pub name: CString,
445436
pub ty: MapType,
446437
pub id: u32,
447438
pub key_size: u32,
@@ -459,14 +450,15 @@ pub struct MapInfo {
459450

460451
impl MapInfo {
461452
fn from_uapi(_fd: i32, s: libbpf_sys::bpf_map_info) -> Option<Self> {
462-
let name = name_arr_to_string(&s.name, "(?)");
453+
// SANITY: `libbpf` should guarantee NUL termination.
454+
let name = util::c_char_slice_to_cstr(&s.name).unwrap();
463455
let ty = match MapType::try_from(s.type_) {
464456
Ok(ty) => ty,
465457
Err(_) => MapType::Unknown,
466458
};
467459

468460
Some(Self {
469-
name,
461+
name: name.to_owned(),
470462
ty,
471463
id: s.id,
472464
key_size: s.key_size,
@@ -495,10 +487,9 @@ gen_info_impl!(
495487

496488
/// Information about BPF type format
497489
#[derive(Debug, Clone)]
498-
#[allow(missing_docs)]
499490
pub struct BtfInfo {
500491
/// The name associated with this btf information in the kernel
501-
pub name: String,
492+
pub name: CString,
502493
/// The raw btf bytes from the kernel
503494
pub btf: Vec<u8>,
504495
/// The btf id associated with this btf information in the kernel
@@ -532,7 +523,10 @@ impl BtfInfo {
532523
util::parse_ret(ret)?;
533524

534525
Ok(BtfInfo {
535-
name: String::from_utf8(name).unwrap_or_else(|_| "(?)".to_string()),
526+
// SANITY: Our buffer contained space for a NUL byte and we set its
527+
// contents to 0. Barring a `libbpf` bug a NUL byte will be
528+
// present.
529+
name: CString::from_vec_with_nul(name).unwrap(),
536530
btf,
537531
id: item.id,
538532
})
@@ -568,7 +562,8 @@ impl Iterator for BtfInfoIter {
568562

569563
match info {
570564
Ok(i) => return Some(i),
571-
Err(e) => eprintln!("Failed to load btf information: {}", e),
565+
// TODO: We should consider bubbling up errors properly.
566+
Err(_err) => (),
572567
}
573568
}
574569
}

libbpf-rs/src/util.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::ffi::CStr;
22
use std::ffi::CString;
3+
use std::mem::transmute;
34
use std::os::raw::c_char;
45
use std::path::Path;
56
use std::ptr::NonNull;
@@ -32,6 +33,22 @@ pub fn c_ptr_to_string(p: *const c_char) -> Result<String> {
3233
.to_owned())
3334
}
3435

36+
/// Convert a `[c_char]` into a `CStr`.
37+
pub fn c_char_slice_to_cstr(s: &[c_char]) -> Option<&CStr> {
38+
// TODO: Switch to using `CStr::from_bytes_until_nul` once we require
39+
// Rust 1.69.0.
40+
let nul_idx = s
41+
.iter()
42+
.enumerate()
43+
.find_map(|(idx, b)| (*b == 0).then_some(idx))?;
44+
let cstr =
45+
// SAFETY: `c_char` and `u8` are both just one byte plain old data
46+
// types.
47+
CStr::from_bytes_with_nul(unsafe { transmute::<&[c_char], &[u8]>(&s[0..=nul_idx]) })
48+
.unwrap();
49+
Some(cstr)
50+
}
51+
3552
/// Round up a number to the next multiple of `r`
3653
pub fn roundup(num: usize, r: usize) -> usize {
3754
((num + (r - 1)) / r) * r
@@ -129,4 +146,27 @@ mod tests {
129146
let num = num_possible_cpus().unwrap();
130147
assert!(num > 0);
131148
}
149+
150+
/// Check that we can convert a `[c_char]` into a `CStr`.
151+
#[test]
152+
fn c_char_slice_conversion() {
153+
let slice = [];
154+
assert_eq!(c_char_slice_to_cstr(&slice), None);
155+
156+
let slice = [0];
157+
assert_eq!(
158+
c_char_slice_to_cstr(&slice).unwrap(),
159+
CStr::from_bytes_with_nul(b"\0").unwrap()
160+
);
161+
162+
let slice = ['a' as _, 'b' as _, 'c' as _, 0 as _];
163+
assert_eq!(
164+
c_char_slice_to_cstr(&slice).unwrap(),
165+
CStr::from_bytes_with_nul(b"abc\0").unwrap()
166+
);
167+
168+
// Missing terminating NUL byte.
169+
let slice = ['a' as _, 'b' as _, 'c' as _];
170+
assert_eq!(c_char_slice_to_cstr(&slice), None);
171+
}
132172
}

0 commit comments

Comments
 (0)