-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
aya+ebpf: Add integration test for reading perf_events from bpf program
- Loading branch information
1 parent
6cba3c5
commit 2458eae
Showing
8 changed files
with
241 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#![no_std] | ||
#![no_main] | ||
|
||
use aya_bpf::{ | ||
bindings::bpf_perf_event_value, | ||
helpers::bpf_get_smp_processor_id, | ||
macros::{map, perf_event}, | ||
maps::PerfEventArray, | ||
programs::PerfEventContext, | ||
}; | ||
|
||
#[repr(C)] | ||
struct EventData { | ||
value: u64, | ||
cpu_id: u32, | ||
tag: u8, | ||
} | ||
|
||
/// Input map: file descriptors of the perf events, obtained by calling | ||
/// `perf_event_open` in user space. | ||
#[map] | ||
static mut DESCRIPTORS: PerfEventArray<i32> = PerfEventArray::with_max_entries(1, 0); | ||
|
||
#[map] | ||
static mut OUTPUT: PerfEventArray<EventData> = PerfEventArray::with_max_entries(1, 0); | ||
|
||
#[perf_event] | ||
pub fn on_perf_event(ctx: PerfEventContext) -> i64 { | ||
match read_event().map(|res| write_output(&ctx, res)) { | ||
Ok(_) => 0, | ||
Err(e) => e, | ||
} | ||
} | ||
|
||
fn read_event() -> Result<EventData, i64> { | ||
// read the event value using the file descriptor in the DESCRIPTORS array | ||
let event: bpf_perf_event_value = unsafe { DESCRIPTORS.read_current_cpu() }?; | ||
|
||
let cpu_id = unsafe { bpf_get_smp_processor_id() }; | ||
let res = EventData { | ||
value: event.counter, | ||
cpu_id, | ||
tag: 0xAB, | ||
}; | ||
Ok(res) | ||
} | ||
|
||
fn write_output(ctx: &PerfEventContext, output: EventData) -> Result<(), i64> { | ||
unsafe { OUTPUT.output_current_cpu(ctx, &output) } | ||
} | ||
|
||
#[cfg(not(test))] | ||
#[panic_handler] | ||
fn panic(_info: &core::panic::PanicInfo) -> ! { | ||
loop {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use std::os::fd::OwnedFd; | ||
|
||
use aya::maps::PerfEventArray; | ||
use aya::programs::perf_event::{perf_event_open, PerfEventScope}; | ||
use aya::programs::{PerfTypeId, SamplePolicy}; | ||
use aya_obj::generated::perf_sw_ids; | ||
use test_log::test; | ||
|
||
#[derive(Debug)] | ||
#[repr(C)] | ||
struct EventData { | ||
value: u64, | ||
cpu_id: u32, | ||
tag: u8, | ||
} | ||
|
||
#[test] | ||
fn perf_event_read_from_kernel() { | ||
// load bpf program | ||
let mut bpf = Bpf::load(PERF_EVENTS); | ||
let mut descriptors = PerfEventArray::<i32>::try_from(bpf.map_mut("DESCRIPTORS").unwrap()).unwrap(); | ||
let mut bpf_output = PerfEventArray::<EventData>::try_from(bpf.map_mut("OUTPUT").unwrap()).unwrap(); | ||
|
||
// open a perf_event | ||
// Beware: this returns an `OwnedFd`, which means that the file descriptor is closed at the end of the scope | ||
let event_fd: OwnedFd = perf_event_open( | ||
PERF_TYPE_HARDWARE, | ||
PERF_COUNT_HW_INSTRUCTIONS, | ||
PerfEventScope::CallingProcessAnyCpu, | ||
None, | ||
None, | ||
0, | ||
) | ||
.unwrap(); | ||
|
||
// pass pointer to bpf array | ||
descriptors.set(0, event_fd.into()); | ||
|
||
// load program | ||
let program: &mut PerfEvent = bpf.program_mut("on_perf_event").unwrap().try_into().unwrap(); | ||
program.load().expect("failed to load the bpf program"); | ||
|
||
// get buffer to poll the events | ||
const BUF_PAGE_COUNT: u32 = 1; | ||
let buf = bpf_output | ||
.open(0, Some(buf_page_count)) | ||
.expect("failed to open output buffer to poll events"); | ||
|
||
// attach program | ||
const CPU_ID: u32 = 0; | ||
program.attach( | ||
PerfTypeId::Software, | ||
perf_sw_ids::PERF_COUNT_SW_CPU_CLOCK as u64, | ||
PerfEventScope::AllProcessesOneCpu { cpu: CPU_ID }, | ||
SamplePolicy::Frequency(1), | ||
); | ||
|
||
// sleep a little bit, then poll the values from the buffer | ||
std::thread::sleep(Duration::from_secs(2)); | ||
assert!( | ||
buf.readable(), | ||
"the buffer should have been filled by the bpf program" | ||
); | ||
|
||
// 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()); | ||
let event_stats = buf | ||
.read_events(&mut events_data) | ||
.expect("failed to poll events"); | ||
|
||
for data_buf in events_data.iter_mut().take(events_stats.read) { | ||
// You must ensure that the definition of the struct (here `EventData`) is the same | ||
// in the userspace and in the bpf program. | ||
let ptr = data_buf.as_ptr() as *const EventData; | ||
let data: EventData = unsafe { ptr.read_unaligned() }; | ||
|
||
assert_eq!(data.cpu_id, CPU_ID, "unexpected data: {}", data); | ||
assert_eq!(data.tag, 0xAB, "unexpected data: {}", data); | ||
} | ||
|
||
Ok(()) | ||
} |