Add Tests

This moves a large chunk of code from ebpf to shared so we can re-use
write_record_header and write_record_message and friends so that we
can write test cases to ensure that logs are properly formatted
given certain input.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
pull/350/head
Dave Tucker 2 years ago
parent 628b473e09
commit 5d82d9a73e

@ -1,5 +1,7 @@
#![no_std] #![no_std]
use core::{cmp, mem, ptr};
pub const LOG_BUF_CAPACITY: usize = 8192; pub const LOG_BUF_CAPACITY: usize = 8192;
pub const LOG_FIELDS: usize = 7; pub const LOG_FIELDS: usize = 7;
@ -71,3 +73,106 @@ mod userspace {
unsafe impl aya::Pod for RecordField {} unsafe impl aya::Pod for RecordField {}
unsafe impl aya::Pod for ArgType {} unsafe impl aya::Pod for ArgType {}
} }
struct TagLenValue<'a, T> {
tag: T,
value: &'a [u8],
}
impl<'a, T> TagLenValue<'a, T>
where
T: Copy,
{
#[inline(always)]
pub(crate) fn new(tag: T, value: &'a [u8]) -> TagLenValue<'a, T> {
TagLenValue { tag, value }
}
pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result<usize, ()> {
let size = mem::size_of::<T>() + mem::size_of::<usize>() + self.value.len();
if buf.len() < size {
return Err(());
}
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) };
buf = &mut buf[mem::size_of::<T>()..];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) };
buf = &mut buf[mem::size_of::<usize>()..];
let len = cmp::min(buf.len(), self.value.len());
buf[..len].copy_from_slice(&self.value[..len]);
Ok(size)
}
}
pub trait WriteToBuf {
#[allow(clippy::result_unit_err)]
fn write(&self, buf: &mut [u8]) -> Result<usize, ()>;
}
macro_rules! impl_write_to_buf {
($type:ident, $arg_type:expr) => {
impl WriteToBuf for $type {
fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::<ArgType>::new($arg_type, &self.to_ne_bytes()).write(buf)
}
}
};
}
impl_write_to_buf!(i8, ArgType::I8);
impl_write_to_buf!(i16, ArgType::I16);
impl_write_to_buf!(i32, ArgType::I32);
impl_write_to_buf!(i64, ArgType::I64);
impl_write_to_buf!(i128, ArgType::I128);
impl_write_to_buf!(isize, ArgType::Isize);
impl_write_to_buf!(u8, ArgType::U8);
impl_write_to_buf!(u16, ArgType::U16);
impl_write_to_buf!(u32, ArgType::U32);
impl_write_to_buf!(u64, ArgType::U64);
impl_write_to_buf!(u128, ArgType::U128);
impl_write_to_buf!(usize, ArgType::Usize);
impl_write_to_buf!(f32, ArgType::F32);
impl_write_to_buf!(f64, ArgType::F64);
impl WriteToBuf for str {
fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::<ArgType>::new(ArgType::Str, self.as_bytes()).write(buf)
}
}
#[allow(clippy::result_unit_err)]
#[doc(hidden)]
#[inline(always)]
pub fn write_record_header(
buf: &mut [u8],
target: &str,
level: Level,
module: &str,
file: &str,
line: u32,
num_args: usize,
) -> Result<usize, ()> {
let mut size = 0;
for attr in [
TagLenValue::<RecordField>::new(RecordField::Target, target.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::Level, &(level as usize).to_ne_bytes()),
TagLenValue::<RecordField>::new(RecordField::Module, module.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::File, file.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::Line, &line.to_ne_bytes()),
TagLenValue::<RecordField>::new(RecordField::NumArgs, &num_args.to_ne_bytes()),
] {
size += attr.write(&mut buf[size..])?;
}
Ok(size)
}
#[allow(clippy::result_unit_err)]
#[doc(hidden)]
pub fn write_record_message(buf: &mut [u8], msg: &str) -> Result<usize, ()> {
TagLenValue::<RecordField>::new(RecordField::Log, msg.as_bytes()).write(buf)
}

@ -19,5 +19,9 @@ log = "0.4"
bytes = "1.1" bytes = "1.1"
tokio = { version = "1.2.0" } tokio = { version = "1.2.0" }
[dev-dependencies]
simplelog = "0.12"
testing_logger = "0.1.1"
[lib] [lib]
path = "src/lib.rs" path = "src/lib.rs"

@ -34,7 +34,7 @@
//! //!
//! With the following eBPF code: //! With the following eBPF code:
//! //!
//! ```no_run //! ```ignore
//! # let ctx = (); //! # let ctx = ();
//! use aya_log_ebpf::{debug, error, info, trace, warn}; //! use aya_log_ebpf::{debug, error, info, trace, warn};
//! //!
@ -328,3 +328,54 @@ impl<'a, T: Pod> TagLenValue<'a, T> {
)) ))
} }
} }
#[cfg(test)]
mod test {
use super::*;
use aya_log_common::{write_record_header, write_record_message, WriteToBuf};
use log::logger;
use testing_logger;
fn new_log(msg: &str, args: usize) -> Result<(usize, Vec<u8>), ()> {
let mut buf = vec![0; 8192];
let mut len = write_record_header(
&mut buf,
"test",
aya_log_common::Level::Info,
"test",
"test.rs",
123,
args,
)?;
len += write_record_message(&mut buf[len..], msg)?;
Ok((len, buf))
}
#[test]
fn test_str() {
testing_logger::setup();
let (_, input) = new_log("test", 0).unwrap();
let logger = logger();
let _ = log_buf(&input, logger);
testing_logger::validate(|captured_logs| {
assert_eq!(captured_logs.len(), 1);
assert_eq!(captured_logs[0].body, "test");
assert_eq!(captured_logs[0].level, Level::Info);
});
}
#[test]
fn test_str_with_args() {
testing_logger::setup();
let (len, mut input) = new_log("hello {}", 1).unwrap();
let name = "test";
(*name).write(&mut input[len..]).unwrap();
let logger = logger();
let _ = log_buf(&input, logger);
testing_logger::validate(|captured_logs| {
assert_eq!(captured_logs.len(), 1);
assert_eq!(captured_logs[0].body, "hello test");
assert_eq!(captured_logs[0].level, Level::Info);
});
}
}

@ -1,13 +1,9 @@
#![no_std] #![no_std]
use core::{cmp, mem, ptr};
use aya_bpf::{ use aya_bpf::{
macros::map, macros::map,
maps::{PerCpuArray, PerfEventByteArray}, maps::{PerCpuArray, PerfEventByteArray}
}; };
use aya_log_common::{ArgType, RecordField}; pub use aya_log_common::{Level, LOG_BUF_CAPACITY, write_record_header, write_record_message, WriteToBuf};
pub use aya_log_common::{Level, LOG_BUF_CAPACITY};
pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn}; pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn};
#[doc(hidden)] #[doc(hidden)]
@ -24,109 +20,6 @@ pub static mut AYA_LOG_BUF: PerCpuArray<LogBuf> = PerCpuArray::with_max_entries(
#[map] #[map]
pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0); pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0);
struct TagLenValue<'a, T> {
tag: T,
value: &'a [u8],
}
impl<'a, T> TagLenValue<'a, T>
where
T: Copy,
{
#[inline(always)]
pub(crate) fn new(tag: T, value: &'a [u8]) -> TagLenValue<'a, T> {
TagLenValue { tag, value }
}
pub(crate) fn write(&self, mut buf: &mut [u8]) -> Result<usize, ()> {
let size = mem::size_of::<T>() + mem::size_of::<usize>() + self.value.len();
if buf.len() < size {
return Err(());
}
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) };
buf = &mut buf[mem::size_of::<T>()..];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) };
buf = &mut buf[mem::size_of::<usize>()..];
let len = cmp::min(buf.len(), self.value.len());
buf[..len].copy_from_slice(&self.value[..len]);
Ok(size)
}
}
pub trait WriteToBuf {
#[allow(clippy::result_unit_err)]
fn write(&self, buf: &mut [u8]) -> Result<usize, ()>;
}
macro_rules! impl_write_to_buf {
($type:ident, $arg_type:expr) => {
impl WriteToBuf for $type {
fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::<ArgType>::new($arg_type, &self.to_ne_bytes()).write(buf)
}
}
};
}
impl_write_to_buf!(i8, ArgType::I8);
impl_write_to_buf!(i16, ArgType::I16);
impl_write_to_buf!(i32, ArgType::I32);
impl_write_to_buf!(i64, ArgType::I64);
impl_write_to_buf!(i128, ArgType::I128);
impl_write_to_buf!(isize, ArgType::Isize);
impl_write_to_buf!(u8, ArgType::U8);
impl_write_to_buf!(u16, ArgType::U16);
impl_write_to_buf!(u32, ArgType::U32);
impl_write_to_buf!(u64, ArgType::U64);
impl_write_to_buf!(u128, ArgType::U128);
impl_write_to_buf!(usize, ArgType::Usize);
impl_write_to_buf!(f32, ArgType::F32);
impl_write_to_buf!(f64, ArgType::F64);
impl WriteToBuf for str {
fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
TagLenValue::<ArgType>::new(ArgType::Str, self.as_bytes()).write(buf)
}
}
#[allow(clippy::result_unit_err)]
#[doc(hidden)]
#[inline(always)]
pub fn write_record_header(
buf: &mut [u8],
target: &str,
level: Level,
module: &str,
file: &str,
line: u32,
num_args: usize,
) -> Result<usize, ()> {
let mut size = 0;
for attr in [
TagLenValue::<RecordField>::new(RecordField::Target, target.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::Level, &(level as usize).to_ne_bytes()),
TagLenValue::<RecordField>::new(RecordField::Module, module.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::File, file.as_bytes()),
TagLenValue::<RecordField>::new(RecordField::Line, &line.to_ne_bytes()),
TagLenValue::<RecordField>::new(RecordField::NumArgs, &num_args.to_ne_bytes()),
] {
size += attr.write(&mut buf[size..])?;
}
Ok(size)
}
#[allow(clippy::result_unit_err)]
#[doc(hidden)]
pub fn write_record_message(buf: &mut [u8], msg: &str) -> Result<usize, ()> {
TagLenValue::<RecordField>::new(RecordField::Log, msg.as_bytes()).write(buf)
}
#[doc(hidden)] #[doc(hidden)]
pub mod macro_support { pub mod macro_support {
pub use aya_log_common::{Level, LOG_BUF_CAPACITY}; pub use aya_log_common::{Level, LOG_BUF_CAPACITY};

Loading…
Cancel
Save