浏览代码

Merge pull request #7 from dave-tucker/main

Add Unit Tests, CI, Dependabot and Linting
Alessandro Decina 2 年之前
父节点
当前提交
b9cc951613

+ 9 - 0
.github/dependabot.yml

@@ -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"

+ 37 - 0
.github/workflows/build-bpf.yml

@@ -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

+ 26 - 0
.github/workflows/build.yml

@@ -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

+ 41 - 0
.github/workflows/lint.yml

@@ -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

+ 3 - 0
.vim/coc-settings.json

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

+ 3 - 0
.vscode/settings.json

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

+ 105 - 0
aya-log-common/src/lib.rs

@@ -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)
+}

+ 4 - 0
aya-log/Cargo.toml

@@ -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"

+ 52 - 1
aya-log/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);
+        });
+    }
+}

+ 11 - 1
ebpf/Cargo.toml

@@ -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"

+ 1 - 1
ebpf/aya-log-ebpf-macros/Cargo.toml

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

+ 0 - 9
ebpf/aya-log-ebpf/Cargo.toml

@@ -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"

+ 3 - 108
ebpf/aya-log-ebpf/src/lib.rs

@@ -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};

+ 13 - 0
ebpf/example/Cargo.toml

@@ -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"

+ 22 - 0
ebpf/example/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() }
+}

+ 4 - 0
ebpf/rustfmt.toml

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

+ 4 - 0
rustfmt.toml

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

+ 1 - 2
xtask/src/build_ebpf.rs

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