You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
aya/test/integration-test/src/tests/hash_map.rs

239 lines
7.5 KiB
Rust

use std::thread;
use aya::{
Ebpf, EbpfLoader,
maps::{Array, HashMap, MapData, MapError, PerCpuHashMap},
programs::UProbe,
};
use integration_common::hash_map::GET_INDEX;
use nix::{
sched::{CpuSet, sched_setaffinity},
unistd::Pid,
};
/// Triggers the eBPF program that inserts the given `key` and `value` pair
/// into the hash map.
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn hash_map_insert(key: u32, value: u32) {
std::hint::black_box((key, value));
}
/// Triggers the eBPF program that retrieves the value associated with the
/// `key` and inserts it into the array.
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn hash_map_get(key: u32) {
std::hint::black_box(key);
}
/// Loads the uprobe program and attaches it to the given `symbol`.
fn load_program(ebpf: &mut Ebpf, prog_name: &str, symbol: &str) {
let prog: &mut UProbe = ebpf.program_mut(prog_name).unwrap().try_into().unwrap();
prog.load().unwrap();
prog.attach(symbol, "/proc/self/exe", None, None).unwrap();
}
/// Loads the pair of programs:
///
/// * `insert_prog` that inserts key and value pairs into the `hash_map`.
/// * `get_prog` that retrieves values from the `hash_map` and inserts them
/// into `result_map`.
///
/// Returns the result array and the hash map.
fn load_programs_with_maps<'a>(
ebpf: &'a mut Ebpf,
result_array: &'a str,
hash_map: &'a str,
insert_prog: &'a str,
get_prog: &'a str,
) -> (Array<&'a MapData, u32>, HashMap<&'a MapData, u32, u32>) {
load_program(ebpf, insert_prog, "hash_map_insert");
load_program(ebpf, get_prog, "hash_map_get");
let result_array = ebpf.map(result_array).unwrap();
let result_array = Array::<_, u32>::try_from(result_array).unwrap();
let hash_map = ebpf.map(hash_map).unwrap();
let hash_map = HashMap::<_, u32, u32>::try_from(hash_map).unwrap();
(result_array, hash_map)
}
/// Loads the `insert_prog` program that inserts elements into the
/// `per_cpu_hash_map`. Returns the map.
fn load_program_with_per_cpu_map<'a>(
ebpf: &'a mut Ebpf,
per_cpu_hash_map: &'a str,
insert_prog: &'a str,
) -> PerCpuHashMap<&'a MapData, u32, u32> {
load_program(ebpf, insert_prog, "hash_map_insert");
let hash_map = ebpf.map(per_cpu_hash_map).unwrap();
PerCpuHashMap::<_, u32, u32>::try_from(hash_map).unwrap()
}
#[test_log::test]
fn test_hash_map() {
let mut ebpf = EbpfLoader::new().load(crate::HASH_MAP).unwrap();
for (result_map_name, hash_map_name, insert_prog_name, get_prog_name) in [
// BTF map definitions.
("RESULT", "HASH_MAP", "hash_map_insert", "hash_map_get"),
// Legacy map definitions.
(
"RESULT_LEGACY",
"HASH_MAP_LEGACY",
"hash_map_insert_legacy",
"hash_map_get_legacy",
),
] {
let (result_array, hash_map) = load_programs_with_maps(
&mut ebpf,
result_map_name,
hash_map_name,
insert_prog_name,
get_prog_name,
);
let seq = 0_u32..9;
for i in seq.clone() {
hash_map_insert(i.pow(2), i);
}
for i in seq.clone() {
// Assert the value returned by user-space API.
let key = i.pow(2);
let value = hash_map.get(&key, 0).unwrap();
assert_eq!(value, i);
// Assert the value returned by eBPF in-kernel API.
hash_map_get(key);
let result = result_array.get(&GET_INDEX, 0).unwrap();
assert_eq!(result, i);
}
}
}
#[test_log::test]
fn test_lru_hash_map() {
let mut ebpf = EbpfLoader::new().load(crate::HASH_MAP).unwrap();
for (result_map_name, hash_map_name, insert_prog_name, get_prog_name) in [
// BTF map definitions.
(
"RESULT",
"LRU_HASH_MAP",
"lru_hash_map_insert",
"lru_hash_map_get",
),
// Legacy map definitions.
(
"RESULT_LEGACY",
"LRU_HASH_MAP_LEGACY",
"lru_hash_map_insert_legacy",
"lru_hash_map_get_legacy",
),
] {
let (result_array, hash_map) = load_programs_with_maps(
&mut ebpf,
result_map_name,
hash_map_name,
insert_prog_name,
get_prog_name,
);
// Insert elements over capacity.
let seq = 0_u32..15;
for i in seq.clone() {
hash_map_insert(i.pow(2), i);
}
// Check whether elements 0..5 got evicted.
for i in 0_u32..5 {
let key = i.pow(2);
assert!(matches!(hash_map.get(&key, 0), Err(MapError::KeyNotFound)));
}
// Check whether the newest 10 elements can be retrieved.
for i in 5_u32..15 {
// Assert the value returned by user-space API.
let key = i.pow(2);
let value = hash_map.get(&key, 0).unwrap();
assert_eq!(value, i);
// Assert the value returned by eBPF in-kernel API.
hash_map_get(key);
let result = result_array.get(&GET_INDEX, 0).unwrap();
assert_eq!(result, i);
}
}
}
#[test_log::test]
fn test_per_cpu_hash_map() {
let mut ebpf = EbpfLoader::new().load(crate::HASH_MAP).unwrap();
for (hash_map_name, insert_prog_name) in [
// BTF map definitions.
("PER_CPU_HASH_MAP", "per_cpu_hash_map_insert"),
// Legacy map definitions.
("PER_CPU_HASH_MAP_LEGACY", "per_cpu_hash_map_insert_legacy"),
] {
let hash_map = load_program_with_per_cpu_map(&mut ebpf, hash_map_name, insert_prog_name);
let seq = 0_u32..9;
thread::scope(|s| {
let seq = seq.clone();
s.spawn(move || {
let mut cpu_set = CpuSet::new();
cpu_set.set(0).unwrap();
sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
for i in seq {
hash_map_insert(i.pow(2), i);
}
});
});
for i in seq.clone() {
let key = i.pow(2);
let values = hash_map.get(&key, 0).unwrap();
assert_eq!(values.first().unwrap(), &i);
}
}
}
#[test_log::test]
fn test_lru_per_cpu_hash_map() {
let mut ebpf = EbpfLoader::new().load(crate::HASH_MAP).unwrap();
for (hash_map_name, insert_prog_name) in [
// BTF map definitions.
("LRU_PER_CPU_HASH_MAP", "lru_per_cpu_hash_map_insert"),
// Legacy map definitions.
(
"LRU_PER_CPU_HASH_MAP_LEGACY",
"lru_per_cpu_hash_map_insert_legacy",
),
] {
let hash_map = load_program_with_per_cpu_map(&mut ebpf, hash_map_name, insert_prog_name);
// Insert elements over capacity.
let seq = 0_u32..15;
thread::scope(|s| {
let seq = seq.clone();
s.spawn(move || {
let mut cpu_set = CpuSet::new();
cpu_set.set(0).unwrap();
sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
for i in seq {
hash_map_insert(i.pow(2), i);
}
});
});
// Check whether elements 0..5 got evicted.
for i in 0_u32..5 {
let key = i.pow(2);
assert!(matches!(hash_map.get(&key, 0), Err(MapError::KeyNotFound)));
}
// Check whether the newest 10 elements can be retrieved.
for i in 5_u32..15 {
let key = i.pow(2);
let values = hash_map.get(&key, 0).unwrap();
assert_eq!(values.first().unwrap(), &i);
}
}
}