mirror of https://github.com/aya-rs/aya
test: add a perf_event breakpoint test
Test perf_event breakpoints by attaching a RW breakpoint to modprobe_path and triggering a read from procfs, asserting that the tgid of the program triggering the breakpoint matches the test program.reviewable/pr1365/r2
parent
f7a9d73d42
commit
8b58fc13fc
@ -0,0 +1,24 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![expect(unused_crate_dependencies, reason = "used in other bins")]
|
||||||
|
|
||||||
|
use aya_ebpf::{
|
||||||
|
EbpfContext as _,
|
||||||
|
macros::{map, perf_event},
|
||||||
|
maps::HashMap,
|
||||||
|
programs::PerfEventContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
extern crate ebpf_panic;
|
||||||
|
|
||||||
|
#[map]
|
||||||
|
static READERS: HashMap<u32, u64> = HashMap::with_max_entries(1, 0);
|
||||||
|
|
||||||
|
#[perf_event]
|
||||||
|
fn perf_event_bp(ctx: PerfEventContext) -> u32 {
|
||||||
|
let tgid = ctx.tgid();
|
||||||
|
let addr = unsafe { (*ctx.ctx).addr };
|
||||||
|
let _ = READERS.insert(tgid, addr, 0);
|
||||||
|
0
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::{BufRead as _, BufReader},
|
||||||
|
};
|
||||||
|
|
||||||
|
use aya::{
|
||||||
|
Ebpf,
|
||||||
|
programs::{
|
||||||
|
PerfEventScope, PerfTypeId, SamplePolicy,
|
||||||
|
perf_event::{
|
||||||
|
PerfBreakpoint, PerfBreakpointSize::HwBreakpointLen1,
|
||||||
|
PerfBreakpointType::HwBreakpointRW,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
util::online_cpus,
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
// Parse /proc/kallsyms and return the address for the given symbol name, if
|
||||||
|
// found.
|
||||||
|
fn find_kallsyms_symbol(sym: &str) -> Option<u64> {
|
||||||
|
let file = File::open("/proc/kallsyms").ok()?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
|
// Format: "<addr> <type> <symbol> [<module>]"
|
||||||
|
let mut parts = line.split_whitespace();
|
||||||
|
let addr_str = parts.next()?;
|
||||||
|
let _type = parts.next()?;
|
||||||
|
let name = parts.next()?;
|
||||||
|
if name == sym
|
||||||
|
&& let Ok(addr) = u64::from_str_radix(addr_str, 16)
|
||||||
|
{
|
||||||
|
return Some(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn perf_event_bp() {
|
||||||
|
let mut bpf = Ebpf::load(crate::PERF_EVENT_BP).unwrap();
|
||||||
|
|
||||||
|
let attach_addr = find_kallsyms_symbol("modprobe_path").unwrap();
|
||||||
|
|
||||||
|
let prog: &mut aya::programs::PerfEvent = bpf
|
||||||
|
.program_mut("perf_event_bp")
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
prog.load().unwrap();
|
||||||
|
|
||||||
|
// attach hardware breakpoint to modprobe_path global
|
||||||
|
for cpu in online_cpus().unwrap() {
|
||||||
|
info!("attaching to cpu {cpu}");
|
||||||
|
prog.attach(
|
||||||
|
PerfTypeId::Breakpoint,
|
||||||
|
0u64,
|
||||||
|
PerfEventScope::AllProcessesOneCpu { cpu },
|
||||||
|
SamplePolicy::Period(1),
|
||||||
|
true,
|
||||||
|
Some(PerfBreakpoint {
|
||||||
|
address: attach_addr,
|
||||||
|
length: HwBreakpointLen1,
|
||||||
|
type_: HwBreakpointRW,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger hardware breakpoint by reading modprobe_path via procfs
|
||||||
|
let _ = fs::read_to_string("/proc/sys/kernel/modprobe");
|
||||||
|
|
||||||
|
// assert that the map contains an entry for this process
|
||||||
|
let map: aya::maps::HashMap<_, u32, u64> =
|
||||||
|
aya::maps::HashMap::try_from(bpf.map_mut("READERS").unwrap()).unwrap();
|
||||||
|
let tgid = std::process::id();
|
||||||
|
let read_addr = map.get(&tgid, 0).unwrap();
|
||||||
|
assert_eq!(read_addr, attach_addr);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue