Merge pull request from dave-tucker/main

Add Unit Tests, CI, Dependabot and Linting
Alessandro Decina committed by GitHub
commit b9cc951613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,9 @@
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"

@ -0,0 +1,37 @@
name: build-bpf
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: rust-src
override: true
- uses: Swatinem/rust-cache@v1
- name: Pre-requisites
run: cargo install bpf-linker
- name: Build
run: |
pushd ebpf
cargo build --verbose
popd

@ -0,0 +1,26 @@
name: build
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v1
- name: Build
run: cargo build --verbose
- name: Run tests
run: RUST_BACKTRACE=full cargo test --verbose

@ -0,0 +1,41 @@
name: lint
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
CARGO_TERM_COLOR: always
jobs:
lint:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
components: rustfmt, clippy, rust-src
override: true
- name: Check formatting
run: |
cargo fmt --all -- --check
pushd ebpf
cargo fmt --all -- --check
popd
- name: Run clippy
run: |
cargo clippy -- --deny warnings
pushd ebpf
cargo clippy -- --deny warnings
popd

@ -0,0 +1,3 @@
{
"rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"]
}

@ -0,0 +1,3 @@
{
"rust-analyzer.linkedProjects": ["Cargo.toml", "ebpf/Cargo.toml"]
}

@ -1,5 +1,7 @@
#![no_std]
use core::{cmp, mem, ptr};
pub const LOG_BUF_CAPACITY: usize = 8192;
pub const LOG_FIELDS: usize = 7;
@ -71,3 +73,106 @@ mod userspace {
unsafe impl aya::Pod for RecordField {}
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"
tokio = { version = "1.2.0" }
[dev-dependencies]
simplelog = "0.12"
testing_logger = "0.1.1"
[lib]
path = "src/lib.rs"

@ -34,7 +34,7 @@
//!
//! With the following eBPF code:
//!
//! ```no_run
//! ```ignore
//! # let ctx = ();
//! 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,2 +1,12 @@
[workspace]
members = ["aya-log-ebpf", "aya-log-ebpf-macros"]
members = ["aya-log-ebpf", "aya-log-ebpf-macros", "example"]
[profile.dev]
panic = "abort"
debug = 1
opt-level = 2
overflow-checks = false
[profile.release]
panic = "abort"

@ -9,4 +9,4 @@ quote = "1.0"
syn = "1.0"
[lib]
proc-macro = true
proc-macro = true

@ -10,12 +10,3 @@ aya-log-ebpf-macros = { path = "../aya-log-ebpf-macros" }
[lib]
path = "src/lib.rs"
[profile.dev]
panic = "abort"
debug = 1
opt-level = 2
overflow-checks = false
[profile.release]
panic = "abort"

@ -1,13 +1,11 @@
#![no_std]
use core::{cmp, mem, ptr};
use aya_bpf::{
macros::map,
maps::{PerCpuArray, PerfEventByteArray},
};
use aya_log_common::{ArgType, RecordField};
pub use aya_log_common::{Level, LOG_BUF_CAPACITY};
pub use aya_log_common::{
write_record_header, write_record_message, Level, WriteToBuf, LOG_BUF_CAPACITY,
};
pub use aya_log_ebpf_macros::{debug, error, info, log, trace, warn};
#[doc(hidden)]
@ -24,109 +22,6 @@ pub static mut AYA_LOG_BUF: PerCpuArray<LogBuf> = PerCpuArray::with_max_entries(
#[map]
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)]
pub mod macro_support {
pub use aya_log_common::{Level, LOG_BUF_CAPACITY};

@ -0,0 +1,13 @@
[package]
name = "example"
version = "0.1.0"
edition = "2018"
publish = false
[dependencies]
aya-bpf = { git = "https://github.com/aya-rs/aya", branch = "main" }
aya-log-ebpf = { path = "../aya-log-ebpf" }
[[bin]]
name = "example"
path = "src/main.rs"

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
use aya_bpf::{macros::tracepoint, programs::TracePointContext, BpfContext};
use aya_log_ebpf::{debug, error, info, trace, warn};
#[tracepoint]
pub fn example(ctx: TracePointContext) -> u32 {
error!(&ctx, "this is an error message 🚨");
warn!(&ctx, "this is a warning message ⚠️");
info!(&ctx, "this is an info message ");
debug!(&ctx, "this is a debug message ️🐝");
trace!(&ctx, "this is a trace message 🔍");
let pid = ctx.pid();
info!(&ctx, "a message with args PID: {}", pid);
0
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
unsafe { core::hint::unreachable_unchecked() }
}

@ -0,0 +1,4 @@
unstable_features = true
reorder_imports = true
imports_granularity = "Crate"

@ -0,0 +1,4 @@
unstable_features = true
reorder_imports = true
imports_granularity = "Crate"

@ -1,5 +1,4 @@
use std::path::PathBuf;
use std::process::Command;
use std::{path::PathBuf, process::Command};
use structopt::StructOpt;

Loading…
Cancel
Save