aya
aya copied to clipboard
aya+ebpf: Implement read+write methods for PerfEventArray
This allows to read and write a PerfEventArray, from userspace and kernel. Thanks to these modifications, one can read perf events from ebpf code!
Usage
- open the perf event from userspace
- Note: this could be done in aya if perf_event_open was public, for now I'm using perf_event_open_sys
- put the event's file descriptor into a map
PerfEventArray<i32>
- load the ebpf program
- from ebpf, read the file descriptor to get the perf event's value, and put it in another map
PerfEventArray<MyValue>
(you can create a struct to carry the info you need, for instance) - from userspace, read the value from the
PerfEventArray
using aPerfEventArrayBuffer
See the integration test for more details.
What is it for?
This can be useful to read performance counters from the kernel space, a method which I have evaluated in my research work.
Deploy Preview for aya-rs-docs failed.
Built without sensitive environment variables
Name | Link |
---|---|
Latest commit | 0aeb379bebde2a7c1b87ec8e0e66713a877daef0 |
Latest deploy log | https://app.netlify.com/sites/aya-rs-docs/deploys/659976617952250008625870 |
Could you write an integration test?
Yes, but how do I open the perf event? Do I make the corresponding functions of aya public, or do I add a dependency?
Sorry, a dependency on what? I think you should make whatever you need public so that your test is representative of real usage.
Sorry, a dependency on what? I think you should make whatever you need public so that your test is representative of real usage.
I've used the crate perf_event_open_sys for step 1 (see the PR's description) to test what I've implemented.
I'll make what I need public :)
@TheElectronWill, this pull request is now in conflict and requires a rebase.
Okay, I've added integration tests!
I've made a user-friendly version of perf_event_open
public, for the purpose of testing and to make the feature available to the users without an additional dependency.
I've failed to run the tests on my machine, with a rather weird error
error: failed to run custom build command for integration-test v0.1.0 (/home/guillaume/Documents/Contrib/aya/test/integration-test)
process didn't exit successfully: /home/guillaume/Documents/Contrib/aya/target/debug/build/integration-test-fd7f5096efa9800e/build-script-build
(exit status: 101)
--- stderr
thread 'main' panicked at test/integration-test/build.rs:258:9:
assertion left == right
failed: cd "[...]/aya/test/integration-ebpf" && CARGO_CFG_BPF_TARGET_ARCH="x86_64" "cargo" "build" "-Z" "build-std=core" "--bins" "--message-format=json" "--release" "--target" "bpfel-unknown-none" "--target-dir" "[...]/aya/target/debug/build/integration-test-8b78b2389a639211/out/integration-ebpf" failed: ExitStatus(unix_wait_status(25856))
left: Some(101)
right: Some(0)
note: run with RUST_BACKTRACE=1
environment variable to display a backtrace
Error: AYA_BUILD_INTEGRATION_BPF="true" "cargo" "build" "--message-format=json" "--package" "integration-test" "--tests" "--profile" "dev" failed: ExitStatus(unix_wait_status(25856))
Running mentioned `cd "..."` command manually reveals the underlying error:
= note: 16:13:28 [ERROR] fatal error: "Inline asm not supported by this streamer because we don't have an asm parser for this target\n" PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace. Stack dump: 0. Running pass 'Function Pass Manager' on module 'bpf_probe_read-8e261d0427f4b668'. 1. Running pass 'BPF Assembly Printer' on function '@test_bpf_probe_read_user_str_bytes'
but I'm stuck here :S Any idea?
</details>
do the integration tests run for you on main
? how are you running them? what versions of rustc and bpf-linker are you using?
Solution to my aforementioned problem: update bpf-linker by running cargo install bpf-linker
.
I've updated the PR to make the new test pass :)
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// if `bpf_map_update_elem` fails. pub fn set(&mut self, index: u32, value: i32) -> Result<(), MapError> {
is value a file descriptor? can we use AsFd instead of a raw i32?
It could be any i32, but that would make no sense for a PerfEventArray
because only an event file descriptor would work with read
. I've used AsFd
.
let mut buf = MaybeUninit::<bpf_perf_event_value>::uninit(); unsafe { // According to the Linux manual, `bpf_perf_event_read_value` is preferred over `bpf_perf_event_read`.
citation?
man bpf-helpers reads:
u64 bpf_perf_event_read(...)
Also, be aware that the newer helper bpf_perf_event_read_value() is recommended over bpf_perf_event_read() in general. The latter has some ABI quirks where error and counter value are used as a return code (which is wrong to do since ranges may overlap). This issue is fixed with bpf_perf_event_read_value(), which at the same time provides more features over the bpf_perf_event_read() interface.
long bpf_perf_event_read_value(...)
In general, bpf_perf_event_read_value() is recommended over bpf_perf_event_read(), which has some ABI issues and provides fewer functionalities.
// sleep a little bit, then poll the values from the buffer std::thread::sleep(Duration::from_secs(2));
this is more than a little bit. why do we need to sleep so long? can we poll the fd instead?
I've changed the test to wait for buf.readable()
to become true. I've also added an async version.
// read the events and check that the returned data is correct let mut events_data: [BytesMut; BUF_PAGE_COUNT] = std::array::from_fn(|_| BytesMut::new());
why does this need more than one of these? i would imagine a single buffer that is reused.
We could use only one, here I wanted to test reading potentially multiple events at once.
Hey @alessandrod, this pull request changes the Aya Public API and requires your review.
@TheElectronWill, this pull request is now in conflict and requires a rebase.
@TheElectronWill, this pull request is now in conflict and requires a rebase.
My hunch is that we should have two separate types - I don't think it's ever desiderable to call open() and set() on the same map, and having both in the same type seems confusing. I'm not sure what the best names would be, maybe PerfEventByteArray and PerfEventArray? Not sure
I agree with the idea. There's already a PerfEventByteArray
at bpf/aya-bpf/src/maps/perf/perf_event_byte_array.rs
, but I'm not sure what is its intended usage.
Hey @alessandrod, this pull request changes the Aya Public API and requires your review.
@TheElectronWill, this pull request is now in conflict and requires a rebase.