-
Notifications
You must be signed in to change notification settings - Fork 149
libbpf-rs: optionally return full vecs from query #269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I don't think that this is a straightforward change so I'd consider this pull request more of an RFC. The tl;dr; is that it's really not valuable to just get the number of instructions if you actually want to introspect. The btf stuff is extra not-valuable for the same reason. This probably ultimately requires actually writing out the code for the iterators instead of a big chunky macro to generate them, but I don't think that's the end of the world. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like these changes, as you said makes for much more usable API. I don't think breaking API change is a big deal, so I'd go ahead with this.
I left few comments on details (e.g., two explicit steps vs a loop), but those are minor. Let's wait for @danielocfb to return to properly review this and land.
Thanks for improving libbpf-rs! :)
libbpf-rs/src/query.rs
Outdated
let item_ptr: *mut libbpf_sys::bpf_prog_info = &mut item; | ||
let mut len = size_of::<libbpf_sys::bpf_prog_info>() as u32; | ||
|
||
loop { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the actual loop looks unnecessary. You shouldn't need more than two calls for bpf_obj_get_info_by_fd: first to get lengths of necessary buffers, second to fill those buffers out by kernel. And then skip the second step if none of options were set. It should also simplify update_vec_field!() macro, probably, as you know that you are resizing vec.
pub id: u32, | ||
pub jited_prog_len: u32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bit conflicted on removing len fields. They seem useful for cases when user doesn't use ProgInfoQueryOptions and wants to do something smarted and custom on their own after fetching lengths. But this might be a bit hypothethical problem... Having vec fields and separate lens doesn't seem like a big deal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this model, the _len is just available by calling .len() on the resulting vector. So I do think it's worth it to just do it the clean way in this instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no strong feelings either way. Unless we have a good use case for the additional length fields this model of just the Vec
carrying the length is fine with me. We label ourselves "opinionated eBPF tooling" after all :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, upon reflection, the reason to do this would be to write an application that only takes the jited instructions if there are <= N of them (for example). This is likely niche enough to ignore but I'll let people argue with me in V2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay. Left some comments. I'd really want for us to try and limit the number of macros; it is borderline unmaintainable. I don't think it's obvious what can be passed in to said macros and there is basically no contract that each argument has to fulfill.
I think that is fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The updated changes look great! Thanks (and thanks for illustrating API usage in the example)! I left a few more minor comments.
Other than that I'd just hope we could add one or more unit tests for the changed functionality. Do you see a way to go about that? At least something like query a program and check non-zero instruction count or similar. Could also include parts of the example. It's mostly that we have something runnable in an automated fashion.
libbpf-rs/src/query.rs
Outdated
if ret != 0 { | ||
return None; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One suggestion: Would it make sense to make the constructor return a Result
and use util::parse_ret
here? We are implicitly and unnecessarily throwing away the error information here, when we should ideally leave that up to callers (with something like Result::ok
) to give them the option of providing more context.
libbpf-rs/src/query.rs
Outdated
#[derive(Clone, Debug)] | ||
/// BTF Line information |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Here and below, can we keep documentation comments above all attributes to keep style consistent with rest of the code base.
#[derive(Clone, Debug)] | |
/// BTF Line information | |
/// BTF Line information | |
#[derive(Clone, Debug)] |
libbpf-rs/src/query.rs
Outdated
if ret != 0 { | ||
return None; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as earlier about propagating errors up.
let mut name: Vec<u8> = Vec::new(); | ||
|
||
let item_ptr: *mut libbpf_sys::bpf_btf_info = &mut item; | ||
let mut len = size_of::<libbpf_sys::bpf_btf_info>() as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let mut len = size_of::<libbpf_sys::bpf_btf_info>() as u32; | |
let mut len = size_of_val(&item) as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixing this instance, but there are others in this file for a subsequent diff
libbpf-rs/src/query.rs
Outdated
pub name: String, | ||
pub btf: Vec<u8>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind terribly documenting these changed members? We try to document everything that is new at the very least in the hopes that eventually we can remove all #[allow(missing_docs)]
.
libbpf-rs/src/query.rs
Outdated
id: s.id, | ||
}) | ||
fn load_from_fd(fd: i32) -> Option<Self> { | ||
let mut item: libbpf_sys::bpf_btf_info = unsafe { std::mem::zeroed() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: let's import std
items we use at the top for the sake of consistency with existing code:
let mut item: libbpf_sys::bpf_btf_info = unsafe { std::mem::zeroed() }; | |
let mut item: libbpf_sys::bpf_btf_info = unsafe { mem::zeroed() }; |
libbpf-rs/src/query.rs
Outdated
unsafe { libbpf_sys::bpf_obj_get_info_by_fd(fd, item_ptr as *mut c_void, &mut len) }; | ||
if ret == 0 { | ||
Some(BtfInfo { | ||
name: String::from_utf8(name).unwrap_or_else(|_| "(?)".to_string()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more suggestion: how about we make BtfInfo::name
a CString
. This way we can forego this opinionated fallible conversion and let users choose how to handle errors and it's barely more than a to_string_lossy
call on their end if they opt not to.
It's what we do in the existing BTF functionality: https://docs.rs/libbpf-rs/0.21.2/libbpf_rs/btf/struct.BtfType.html#method.name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what it's worth, ProgramInfo::name is a String as well. Should that be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed that. Yes, that would be great. Though I won't insist you do it as you didn't really touch that member. So I'll leave it up to you. I added it to my list of things to do as well.
NOTE: this is totally API-breaking, but the initial API wasn't really useful. This avoids allocation overhead and guessing by reading the values and re-invoking the system call. Each array is blocked by a feature flag from the iterator. The bpf_query example now adds the ability to dump disassembled code on x86-64 via iced-x86.
Fetch the actual data from the kernel. In this case, partial fetches of either name or btf data don't make sense, so we haven't implemented the Options pattern as we did for the BpfProgIter
I agree the lack of testing is a problem, but I propose we provide it in another diff. The real path here is to write cfg(test) stubs for the libbpf calls and mock it up completely, but that will be a monster change. Absent that, you end up with tests that need sudo, which isn't super necessary/desirable here IMHO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems mostly fine now. I don't think you addressed all the points or perhaps I dreamed about pointing them out or GitHub swallowed them. Will create a fixup commit and add a CHANGELOG entry, but merging this change. Thanks!
|
||
match info { | ||
Ok(i) => return Some(i), | ||
Err(e) => eprintln!("Failed to load btf information: {}", e), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's please not print stuff in a library. It will end up in callers without any context in places where it is unlikely to be expected.
pub btf: u64, | ||
pub btf_size: u32, | ||
/// The name associated with this btf information in the kernel | ||
pub name: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I thought we agreed on making this CString
. But now not even the comment shows up anymore...
Half our tests already require |
This change addresses a few remaining issues of #269 and adds a CHANGELOG entry. Signed-off-by: Daniel Müller <[email protected]>
This change adds a CHANGELOG entry for #541. Signed-off-by: Daniel Müller <[email protected]>
NOTE: this is totally API-breaking, but the initial API wasn't really useful.
This avoids allocation overhead and guessing by reading the values and re-invoking the system call. Each array is blocked by a feature flag from the iterator.
Here's an example program using iced-x86 to disassembly the jited code: