123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- use std::sync::{Arc, LockResult, Mutex, MutexGuard};
- use aya::{include_bytes_aligned, programs::UProbe, Bpf};
- use aya_log::BpfLogger;
- use log::{Level, Log, Record};
- use tokio::time::{sleep, Duration};
- const MAX_ATTEMPTS: usize = 10;
- const TIMEOUT_MS: u64 = 10;
- #[no_mangle]
- #[inline(never)]
- pub extern "C" fn trigger_ebpf_program() {}
- struct CapturedLogs(Arc<Mutex<Vec<CapturedLog>>>);
- impl CapturedLogs {
- fn with_capacity(capacity: usize) -> Self {
- Self(Arc::new(Mutex::new(Vec::with_capacity(capacity))))
- }
- fn clone(&self) -> Self {
- Self(self.0.clone())
- }
- fn lock(&self) -> LockResult<MutexGuard<'_, Vec<CapturedLog>>> {
- self.0.lock()
- }
- async fn wait_expected_len(&self, expected_len: usize) {
- for _ in 0..MAX_ATTEMPTS {
- {
- let captured_logs = self.0.lock().expect("Failed to lock captured logs");
- if captured_logs.len() == expected_len {
- return;
- }
- }
- sleep(Duration::from_millis(TIMEOUT_MS)).await;
- }
- panic!(
- "Expected {} captured logs, but got {}",
- expected_len,
- self.0.lock().unwrap().len()
- );
- }
- }
- struct CapturedLog {
- pub body: String,
- pub level: Level,
- pub target: String,
- }
- struct TestingLogger {
- captured_logs: CapturedLogs,
- }
- impl TestingLogger {
- pub fn with_capacity(capacity: usize) -> (Self, CapturedLogs) {
- let captured_logs = CapturedLogs::with_capacity(capacity);
- (
- Self {
- captured_logs: captured_logs.clone(),
- },
- captured_logs,
- )
- }
- }
- impl Log for TestingLogger {
- fn enabled(&self, _metadata: &log::Metadata) -> bool {
- true
- }
- fn flush(&self) {}
- fn log(&self, record: &Record) {
- let captured_record = CapturedLog {
- body: format!("{}", record.args()),
- level: record.level(),
- target: record.target().to_string(),
- };
- self.captured_logs
- .lock()
- .expect("Failed to acquire a lock for storing a log")
- .push(captured_record);
- }
- }
- #[tokio::test]
- async fn log() {
- let bytes = include_bytes_aligned!("../../../target/bpfel-unknown-none/release/log");
- let mut bpf = Bpf::load(bytes).unwrap();
- let (logger, captured_logs) = TestingLogger::with_capacity(5);
- BpfLogger::init_with_logger(&mut bpf, logger).unwrap();
- let prog: &mut UProbe = bpf.program_mut("test_log").unwrap().try_into().unwrap();
- prog.load().unwrap();
- prog.attach(Some("trigger_ebpf_program"), 0, "/proc/self/exe", None)
- .unwrap();
- // Call the function that the uprobe is attached to, so it starts logging.
- trigger_ebpf_program();
- captured_logs.wait_expected_len(6).await;
- let records = captured_logs
- .lock()
- .expect("Failed to acquire a lock for reading logs");
- assert_eq!(records.len(), 6);
- assert_eq!(records[0].body, "Hello from eBPF!");
- assert_eq!(records[0].level, Level::Debug);
- assert_eq!(records[0].target, "log");
- assert_eq!(records[1].body, "69, 420, wao");
- assert_eq!(records[1].level, Level::Error);
- assert_eq!(records[1].target, "log");
- assert_eq!(records[2].body, "ipv4: 10.0.0.1, ipv6: 2001:db8::1");
- assert_eq!(records[2].level, Level::Info);
- assert_eq!(records[2].target, "log");
- assert_eq!(
- records[3].body,
- "mac lc: 04:20:06:09:00:40, mac uc: 04:20:06:09:00:40"
- );
- assert_eq!(records[3].level, Level::Trace);
- assert_eq!(records[3].target, "log");
- assert_eq!(records[4].body, "hex lc: 2f, hex uc: 2F");
- assert_eq!(records[4].level, Level::Warn);
- assert_eq!(records[4].target, "log");
- assert_eq!(records[5].body, "hex lc: deadbeef, hex uc: DEADBEEF");
- assert_eq!(records[5].level, Level::Debug);
- assert_eq!(records[5].target, "log");
- }
|