Browse Source

Merge pull request #585 from probulate/tag-len-value

aya-log-common: support logging byte slices
Alessandro Decina 1 year ago
parent
commit
5165bf2

+ 1 - 1
.github/workflows/build-aya.yml

@@ -32,7 +32,7 @@ jobs:
 
       - uses: taiki-e/install-action@cargo-hack
       - name: Check
-        run: cargo hack check --feature-powerset --ignore-private
+        run: cargo hack check --all-targets --feature-powerset --ignore-private
 
       - uses: Swatinem/rust-cache@v1
       - name: Prereqs

+ 91 - 59
aya-log-common/src/lib.rs

@@ -1,6 +1,6 @@
 #![no_std]
 
-use core::{cmp, mem, ptr, slice};
+use core::{mem, num, ptr};
 
 use num_enum::IntoPrimitive;
 
@@ -8,8 +8,10 @@ pub const LOG_BUF_CAPACITY: usize = 8192;
 
 pub const LOG_FIELDS: usize = 6;
 
-#[repr(usize)]
-#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
+pub type LogValueLength = u16;
+
+#[repr(u8)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
 pub enum Level {
     /// The "error" level.
     ///
@@ -33,7 +35,7 @@ pub enum Level {
     Trace,
 }
 
-#[repr(usize)]
+#[repr(u8)]
 #[derive(Copy, Clone, Debug)]
 pub enum RecordField {
     Target = 1,
@@ -46,7 +48,7 @@ pub enum RecordField {
 
 /// Types which are supported by aya-log and can be safely sent from eBPF
 /// programs to userspace.
-#[repr(usize)]
+#[repr(u8)]
 #[derive(Copy, Clone, Debug)]
 pub enum Argument {
     DisplayHint,
@@ -73,6 +75,7 @@ pub enum Argument {
     /// `[u16; 8]` array which represents an IPv6 address.
     ArrU16Len8,
 
+    Bytes,
     Str,
 }
 
@@ -96,55 +99,65 @@ pub enum DisplayHint {
     UpperMac,
 }
 
-struct TagLenValue<'a, T> {
-    tag: T,
-    value: &'a [u8],
+struct TagLenValue<T, V> {
+    pub tag: T,
+    pub value: V,
 }
 
-impl<'a, T> TagLenValue<'a, T>
+impl<T, V> TagLenValue<T, V>
 where
-    T: Copy,
+    V: IntoIterator<Item = u8>,
+    <V as IntoIterator>::IntoIter: ExactSizeIterator,
 {
-    #[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();
-        let remaining = cmp::min(buf.len(), LOG_BUF_CAPACITY);
-        // Check if the size doesn't exceed the buffer bounds.
-        if size > remaining {
+    pub(crate) fn write(self, mut buf: &mut [u8]) -> Result<usize, ()> {
+        // Break the abstraction to please the verifier.
+        if buf.len() > LOG_BUF_CAPACITY {
+            buf = &mut buf[..LOG_BUF_CAPACITY];
+        }
+        let Self { tag, value } = self;
+        let value = value.into_iter();
+        let len = value.len();
+        let wire_len: LogValueLength = value
+            .len()
+            .try_into()
+            .map_err(|num::TryFromIntError { .. }| ())?;
+        let size = mem::size_of_val(&tag) + mem::size_of_val(&wire_len) + len;
+        if size > buf.len() {
             return Err(());
         }
 
-        unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.tag) };
-        buf = &mut buf[mem::size_of::<T>()..];
+        let tag_size = mem::size_of_val(&tag);
+        unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, tag) };
+        buf = &mut buf[tag_size..];
 
-        unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, self.value.len()) };
-        buf = &mut buf[mem::size_of::<usize>()..];
+        unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, wire_len) };
+        buf = &mut buf[mem::size_of_val(&wire_len)..];
+
+        buf.iter_mut().zip(value).for_each(|(dst, src)| {
+            *dst = src;
+        });
 
-        let len = cmp::min(buf.len(), self.value.len());
-        // The verifier isn't happy with `len` being unbounded, so compare it
-        // with `LOG_BUF_CAPACITY`.
-        if len > LOG_BUF_CAPACITY {
-            return Err(());
-        }
-        buf[..len].copy_from_slice(&self.value[..len]);
         Ok(size)
     }
 }
 
+impl<T, V> TagLenValue<T, V> {
+    #[inline(always)]
+    pub(crate) fn new(tag: T, value: V) -> TagLenValue<T, V> {
+        TagLenValue { tag, value }
+    }
+}
+
 pub trait WriteToBuf {
     #[allow(clippy::result_unit_err)]
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()>;
+    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::<Argument>::new($arg_type, &self.to_ne_bytes()).write(buf)
+            fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+                TagLenValue::new($arg_type, self.to_ne_bytes()).write(buf)
             }
         }
     };
@@ -166,35 +179,40 @@ impl_write_to_buf!(f32, Argument::F32);
 impl_write_to_buf!(f64, Argument::F64);
 
 impl WriteToBuf for [u8; 16] {
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        TagLenValue::<Argument>::new(Argument::ArrU8Len16, self).write(buf)
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        TagLenValue::new(Argument::ArrU8Len16, self).write(buf)
     }
 }
 
 impl WriteToBuf for [u16; 8] {
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        let ptr = self.as_ptr().cast::<u8>();
-        let bytes = unsafe { slice::from_raw_parts(ptr, 16) };
-        TagLenValue::<Argument>::new(Argument::ArrU16Len8, bytes).write(buf)
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        let bytes = unsafe { core::mem::transmute::<_, [u8; 16]>(self) };
+        TagLenValue::new(Argument::ArrU16Len8, bytes).write(buf)
     }
 }
 
 impl WriteToBuf for [u8; 6] {
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        TagLenValue::<Argument>::new(Argument::ArrU8Len6, self).write(buf)
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        TagLenValue::new(Argument::ArrU8Len6, self).write(buf)
+    }
+}
+
+impl WriteToBuf for &[u8] {
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        TagLenValue::new(Argument::Bytes, self.iter().copied()).write(buf)
     }
 }
 
-impl WriteToBuf for str {
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        TagLenValue::<Argument>::new(Argument::Str, self.as_bytes()).write(buf)
+impl WriteToBuf for &str {
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        TagLenValue::new(Argument::Str, self.as_bytes().iter().copied()).write(buf)
     }
 }
 
 impl WriteToBuf for DisplayHint {
-    fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        let v: u8 = (*self).into();
-        TagLenValue::<Argument>::new(Argument::DisplayHint, &v.to_ne_bytes()).write(buf)
+    fn write(self, buf: &mut [u8]) -> Result<usize, ()> {
+        let v: u8 = self.into();
+        TagLenValue::new(Argument::DisplayHint, v.to_ne_bytes()).write(buf)
     }
 }
 
@@ -210,17 +228,31 @@ pub fn write_record_header(
     line: u32,
     num_args: usize,
 ) -> Result<usize, ()> {
+    let level: u8 = level.into();
     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..])?;
-    }
-
+    size += TagLenValue::new(RecordField::Target, target.as_bytes().iter().copied())
+        .write(&mut buf[size..])?;
+    size += TagLenValue::new(RecordField::Level, level.to_ne_bytes()).write(&mut buf[size..])?;
+    size += TagLenValue::new(RecordField::Module, module.as_bytes().iter().copied())
+        .write(&mut buf[size..])?;
+    size += TagLenValue::new(RecordField::File, file.as_bytes().iter().copied())
+        .write(&mut buf[size..])?;
+    size += TagLenValue::new(RecordField::Line, line.to_ne_bytes()).write(&mut buf[size..])?;
+    size +=
+        TagLenValue::new(RecordField::NumArgs, num_args.to_ne_bytes()).write(&mut buf[size..])?;
     Ok(size)
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    fn log_value_length_sufficient() {
+        assert!(
+            LOG_BUF_CAPACITY >= LogValueLength::MAX.into(),
+            "{} < {}",
+            LOG_BUF_CAPACITY,
+            LogValueLength::MAX
+        );
+    }
+}

+ 1 - 2
aya-log-ebpf-macros/src/expand.rs

@@ -151,12 +151,11 @@ pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStre
                     let record_len = header_len;
 
                     if let Ok(record_len) = {
-                        use ::aya_log_ebpf::WriteToBuf;
                         Ok::<_, ()>(record_len) #( .and_then(|record_len| {
                             if record_len >= buf.buf.len() {
                                 return Err(());
                             }
-                            { #values_iter }.write(&mut buf.buf[record_len..]).map(|len| record_len + len)
+                            aya_log_ebpf::WriteToBuf::write({ #values_iter }, &mut buf.buf[record_len..]).map(|len| record_len + len)
                         }) )*
                     } {
                         unsafe { ::aya_log_ebpf::AYA_LOGS.output(

+ 191 - 92
aya-log/src/lib.rs

@@ -59,9 +59,11 @@ use std::{
 
 const MAP_NAME: &str = "AYA_LOGS";
 
-use aya_log_common::{Argument, DisplayHint, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS};
+use aya_log_common::{
+    Argument, DisplayHint, Level, LogValueLength, RecordField, LOG_BUF_CAPACITY, LOG_FIELDS,
+};
 use bytes::BytesMut;
-use log::{error, Level, Log, Record};
+use log::{error, Log, Record};
 use thiserror::Error;
 
 use aya::{
@@ -116,9 +118,7 @@ impl BpfLogger {
 
             let log = logger.clone();
             tokio::spawn(async move {
-                let mut buffers = (0..10)
-                    .map(|_| BytesMut::with_capacity(LOG_BUF_CAPACITY))
-                    .collect::<Vec<_>>();
+                let mut buffers = vec![BytesMut::with_capacity(LOG_BUF_CAPACITY); 10];
 
                 loop {
                     let events = buf.read_events(&mut buffers).await.unwrap();
@@ -160,6 +160,20 @@ where
     }
 }
 
+pub struct LowerHexDebugFormatter;
+impl<T> Formatter<&[T]> for LowerHexDebugFormatter
+where
+    T: LowerHex,
+{
+    fn format(v: &[T]) -> String {
+        let mut s = String::new();
+        for v in v {
+            let () = core::fmt::write(&mut s, format_args!("{v:x}")).unwrap();
+        }
+        s
+    }
+}
+
 pub struct UpperHexFormatter;
 impl<T> Formatter<T> for UpperHexFormatter
 where
@@ -170,6 +184,20 @@ where
     }
 }
 
+pub struct UpperHexDebugFormatter;
+impl<T> Formatter<&[T]> for UpperHexDebugFormatter
+where
+    T: UpperHex,
+{
+    fn format(v: &[T]) -> String {
+        let mut s = String::new();
+        for v in v {
+            let () = core::fmt::write(&mut s, format_args!("{v:X}")).unwrap();
+        }
+        s
+    }
+}
+
 pub struct Ipv4Formatter;
 impl<T> Formatter<T> for Ipv4Formatter
 where
@@ -214,9 +242,19 @@ trait Format {
     fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()>;
 }
 
+impl Format for &[u8] {
+    fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
+        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
+            Some(DisplayHint::LowerHex) => Ok(LowerHexDebugFormatter::format(self)),
+            Some(DisplayHint::UpperHex) => Ok(UpperHexDebugFormatter::format(self)),
+            _ => Err(()),
+        }
+    }
+}
+
 impl Format for u32 {
     fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-        match last_hint.map(|h| h.0) {
+        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
             Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
             Some(DisplayHint::LowerHex) => Ok(LowerHexFormatter::format(self)),
             Some(DisplayHint::UpperHex) => Ok(UpperHexFormatter::format(self)),
@@ -231,7 +269,7 @@ impl Format for u32 {
 
 impl Format for [u8; 6] {
     fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-        match last_hint.map(|h| h.0) {
+        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
             Some(DisplayHint::Default) => Err(()),
             Some(DisplayHint::LowerHex) => Err(()),
             Some(DisplayHint::UpperHex) => Err(()),
@@ -246,7 +284,7 @@ impl Format for [u8; 6] {
 
 impl Format for [u8; 16] {
     fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-        match last_hint.map(|h| h.0) {
+        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
             Some(DisplayHint::Default) => Err(()),
             Some(DisplayHint::LowerHex) => Err(()),
             Some(DisplayHint::UpperHex) => Err(()),
@@ -261,7 +299,7 @@ impl Format for [u8; 16] {
 
 impl Format for [u16; 8] {
     fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-        match last_hint.map(|h| h.0) {
+        match last_hint.map(|DisplayHintWrapper(dh)| dh) {
             Some(DisplayHint::Default) => Err(()),
             Some(DisplayHint::LowerHex) => Err(()),
             Some(DisplayHint::UpperHex) => Err(()),
@@ -278,7 +316,7 @@ macro_rules! impl_format {
     ($type:ident) => {
         impl Format for $type {
             fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-                match last_hint.map(|h| h.0) {
+                match last_hint.map(|DisplayHintWrapper(dh)| dh) {
                     Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
                     Some(DisplayHint::LowerHex) => Ok(LowerHexFormatter::format(self)),
                     Some(DisplayHint::UpperHex) => Ok(UpperHexFormatter::format(self)),
@@ -308,7 +346,7 @@ macro_rules! impl_format_float {
     ($type:ident) => {
         impl Format for $type {
             fn format(&self, last_hint: Option<DisplayHintWrapper>) -> Result<String, ()> {
-                match last_hint.map(|h| h.0) {
+                match last_hint.map(|DisplayHintWrapper(dh)| dh) {
                     Some(DisplayHint::Default) => Ok(DefaultFormatter::format(self)),
                     Some(DisplayHint::LowerHex) => Err(()),
                     Some(DisplayHint::UpperHex) => Err(()),
@@ -360,33 +398,42 @@ pub enum Error {
 
 fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> {
     let mut target = None;
-    let mut level = Level::Trace;
+    let mut level = None;
     let mut module = None;
     let mut file = None;
     let mut line = None;
     let mut num_args = None;
 
     for _ in 0..LOG_FIELDS {
-        let (attr, rest) = unsafe { TagLenValue::<'_, RecordFieldWrapper>::try_read(buf)? };
+        let (RecordFieldWrapper(tag), value, rest) = try_read(buf)?;
 
-        match attr.tag.0 {
+        match tag {
             RecordField::Target => {
-                target = Some(std::str::from_utf8(attr.value).map_err(|_| ())?);
+                target = Some(str::from_utf8(value).map_err(|_| ())?);
             }
             RecordField::Level => {
-                level = unsafe { ptr::read_unaligned(attr.value.as_ptr() as *const _) }
+                level = Some({
+                    let level = unsafe { ptr::read_unaligned(value.as_ptr() as *const _) };
+                    match level {
+                        Level::Error => log::Level::Error,
+                        Level::Warn => log::Level::Warn,
+                        Level::Info => log::Level::Info,
+                        Level::Debug => log::Level::Debug,
+                        Level::Trace => log::Level::Trace,
+                    }
+                })
             }
             RecordField::Module => {
-                module = Some(std::str::from_utf8(attr.value).map_err(|_| ())?);
+                module = Some(str::from_utf8(value).map_err(|_| ())?);
             }
             RecordField::File => {
-                file = Some(std::str::from_utf8(attr.value).map_err(|_| ())?);
+                file = Some(str::from_utf8(value).map_err(|_| ())?);
             }
             RecordField::Line => {
-                line = Some(u32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?));
+                line = Some(u32::from_ne_bytes(value.try_into().map_err(|_| ())?));
             }
             RecordField::NumArgs => {
-                num_args = Some(usize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?));
+                num_args = Some(usize::from_ne_bytes(value.try_into().map_err(|_| ())?));
             }
         }
 
@@ -396,101 +443,104 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> {
     let mut full_log_msg = String::new();
     let mut last_hint: Option<DisplayHintWrapper> = None;
     for _ in 0..num_args.ok_or(())? {
-        let (attr, rest) = unsafe { TagLenValue::<'_, ArgumentWrapper>::try_read(buf)? };
+        let (ArgumentWrapper(tag), value, rest) = try_read(buf)?;
 
-        match attr.tag.0 {
+        match tag {
             Argument::DisplayHint => {
-                last_hint = Some(unsafe { ptr::read_unaligned(attr.value.as_ptr() as *const _) });
+                last_hint = Some(unsafe { ptr::read_unaligned(value.as_ptr() as *const _) });
             }
             Argument::I8 => {
                 full_log_msg.push_str(
-                    &i8::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &i8::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::I16 => {
                 full_log_msg.push_str(
-                    &i16::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &i16::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::I32 => {
                 full_log_msg.push_str(
-                    &i32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &i32::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::I64 => {
                 full_log_msg.push_str(
-                    &i64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &i64::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::Isize => {
                 full_log_msg.push_str(
-                    &isize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &isize::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::U8 => {
                 full_log_msg.push_str(
-                    &u8::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &u8::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::U16 => {
                 full_log_msg.push_str(
-                    &u16::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &u16::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::U32 => {
                 full_log_msg.push_str(
-                    &u32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &u32::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::U64 => {
                 full_log_msg.push_str(
-                    &u64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &u64::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::Usize => {
                 full_log_msg.push_str(
-                    &usize::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &usize::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::F32 => {
                 full_log_msg.push_str(
-                    &f32::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &f32::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::F64 => {
                 full_log_msg.push_str(
-                    &f64::from_ne_bytes(attr.value.try_into().map_err(|_| ())?)
+                    &f64::from_ne_bytes(value.try_into().map_err(|_| ())?)
                         .format(last_hint.take())?,
                 );
             }
             Argument::ArrU8Len6 => {
-                let value: [u8; 6] = attr.value.try_into().map_err(|_| ())?;
+                let value: [u8; 6] = value.try_into().map_err(|_| ())?;
                 full_log_msg.push_str(&value.format(last_hint.take())?);
             }
             Argument::ArrU8Len16 => {
-                let value: [u8; 16] = attr.value.try_into().map_err(|_| ())?;
+                let value: [u8; 16] = value.try_into().map_err(|_| ())?;
                 full_log_msg.push_str(&value.format(last_hint.take())?);
             }
             Argument::ArrU16Len8 => {
-                let data: [u8; 16] = attr.value.try_into().map_err(|_| ())?;
+                let data: [u8; 16] = value.try_into().map_err(|_| ())?;
                 let mut value: [u16; 8] = Default::default();
                 for (i, s) in data.chunks_exact(2).enumerate() {
                     value[i] = ((s[1] as u16) << 8) | s[0] as u16;
                 }
                 full_log_msg.push_str(&value.format(last_hint.take())?);
             }
-            Argument::Str => match str::from_utf8(attr.value) {
+            Argument::Bytes => {
+                full_log_msg.push_str(&value.format(last_hint.take())?);
+            }
+            Argument::Str => match str::from_utf8(value) {
                 Ok(v) => {
                     full_log_msg.push_str(v);
                 }
@@ -505,7 +555,7 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> {
         &Record::builder()
             .args(format_args!("{full_log_msg}"))
             .target(target.ok_or(())?)
-            .level(level)
+            .level(level.ok_or(())?)
             .module_path(module)
             .file(file)
             .line(line)
@@ -515,42 +565,32 @@ fn log_buf(mut buf: &[u8], logger: &dyn Log) -> Result<(), ()> {
     Ok(())
 }
 
-struct TagLenValue<'a, T: Pod> {
-    tag: T,
-    value: &'a [u8],
-}
-
-impl<'a, T: Pod> TagLenValue<'a, T> {
-    unsafe fn try_read(mut buf: &'a [u8]) -> Result<(TagLenValue<'a, T>, &'a [u8]), ()> {
-        if buf.len() < mem::size_of::<T>() + mem::size_of::<usize>() {
-            return Err(());
-        }
-
-        let tag = ptr::read_unaligned(buf.as_ptr() as *const T);
-        buf = &buf[mem::size_of::<T>()..];
+fn try_read<T: Pod>(mut buf: &[u8]) -> Result<(T, &[u8], &[u8]), ()> {
+    if buf.len() < mem::size_of::<T>() + mem::size_of::<LogValueLength>() {
+        return Err(());
+    }
 
-        let len = usize::from_ne_bytes(buf[..mem::size_of::<usize>()].try_into().unwrap());
-        buf = &buf[mem::size_of::<usize>()..];
+    let tag = unsafe { ptr::read_unaligned(buf.as_ptr() as *const T) };
+    buf = &buf[mem::size_of::<T>()..];
 
-        if buf.len() < len {
-            return Err(());
-        }
+    let len =
+        LogValueLength::from_ne_bytes(buf[..mem::size_of::<LogValueLength>()].try_into().unwrap());
+    buf = &buf[mem::size_of::<LogValueLength>()..];
 
-        Ok((
-            TagLenValue {
-                tag,
-                value: &buf[..len],
-            },
-            &buf[len..],
-        ))
+    let len: usize = len.into();
+    if buf.len() < len {
+        return Err(());
     }
+
+    let (value, rest) = buf.split_at(len);
+    Ok((tag, value, rest))
 }
 
 #[cfg(test)]
 mod test {
     use super::*;
     use aya_log_common::{write_record_header, WriteToBuf};
-    use log::logger;
+    use log::{logger, Level};
 
     fn new_log(args: usize) -> Result<(usize, Vec<u8>), ()> {
         let mut buf = vec![0; 8192];
@@ -569,14 +609,14 @@ mod test {
     #[test]
     fn test_str() {
         testing_logger::setup();
-        let (len, mut input) = new_log(1).unwrap();
+        let (mut len, mut input) = new_log(1).unwrap();
 
-        "test"
-            .write(&mut input[len..])
-            .expect("could not write to the buffer");
+        len += "test".write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "test");
@@ -589,13 +629,13 @@ mod test {
         testing_logger::setup();
         let (mut len, mut input) = new_log(2).unwrap();
 
-        len += "hello "
-            .write(&mut input[len..])
-            .expect("could not write to the buffer");
-        "test".write(&mut input[len..]).unwrap();
+        len += "hello ".write(&mut input[len..]).unwrap();
+        len += "test".write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "hello test");
@@ -603,6 +643,49 @@ mod test {
         });
     }
 
+    #[test]
+    fn test_bytes() {
+        testing_logger::setup();
+        let (mut len, mut input) = new_log(2).unwrap();
+
+        len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
+        len += [0xde, 0xad].write(&mut input[len..]).unwrap();
+
+        _ = len;
+
+        let logger = logger();
+        let () = log_buf(&input, logger).unwrap();
+        testing_logger::validate(|captured_logs| {
+            assert_eq!(captured_logs.len(), 1);
+            assert_eq!(captured_logs[0].body, "dead");
+            assert_eq!(captured_logs[0].level, Level::Info);
+        });
+    }
+
+    #[test]
+    fn test_bytes_with_args() {
+        testing_logger::setup();
+        let (mut len, mut input) = new_log(5).unwrap();
+
+        len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
+        len += [0xde, 0xad].write(&mut input[len..]).unwrap();
+
+        len += " ".write(&mut input[len..]).unwrap();
+
+        len += DisplayHint::UpperHex.write(&mut input[len..]).unwrap();
+        len += [0xbe, 0xef].write(&mut input[len..]).unwrap();
+
+        _ = len;
+
+        let logger = logger();
+        let () = log_buf(&input, logger).unwrap();
+        testing_logger::validate(|captured_logs| {
+            assert_eq!(captured_logs.len(), 1);
+            assert_eq!(captured_logs[0].body, "dead BEEF");
+            assert_eq!(captured_logs[0].level, Level::Info);
+        });
+    }
+
     #[test]
     fn test_display_hint_default() {
         testing_logger::setup();
@@ -610,10 +693,12 @@ mod test {
 
         len += "default hint: ".write(&mut input[len..]).unwrap();
         len += DisplayHint::Default.write(&mut input[len..]).unwrap();
-        14.write(&mut input[len..]).unwrap();
+        len += 14.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "default hint: 14");
@@ -628,10 +713,12 @@ mod test {
 
         len += "lower hex: ".write(&mut input[len..]).unwrap();
         len += DisplayHint::LowerHex.write(&mut input[len..]).unwrap();
-        200.write(&mut input[len..]).unwrap();
+        len += 200.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "lower hex: c8");
@@ -646,10 +733,12 @@ mod test {
 
         len += "upper hex: ".write(&mut input[len..]).unwrap();
         len += DisplayHint::UpperHex.write(&mut input[len..]).unwrap();
-        200.write(&mut input[len..]).unwrap();
+        len += 200.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "upper hex: C8");
@@ -665,10 +754,12 @@ mod test {
         len += "ipv4: ".write(&mut input[len..]).unwrap();
         len += DisplayHint::Ipv4.write(&mut input[len..]).unwrap();
         // 10.0.0.1 as u32
-        167772161u32.write(&mut input[len..]).unwrap();
+        len += 167772161u32.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "ipv4: 10.0.0.1");
@@ -688,10 +779,12 @@ mod test {
             0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
             0x00, 0x01,
         ];
-        ipv6_arr.write(&mut input[len..]).unwrap();
+        len += ipv6_arr.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
@@ -710,10 +803,12 @@ mod test {
         let ipv6_arr: [u16; 8] = [
             0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001,
         ];
-        ipv6_arr.write(&mut input[len..]).unwrap();
+        len += ipv6_arr.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "ipv6: 2001:db8::1:1");
@@ -730,10 +825,12 @@ mod test {
         len += DisplayHint::LowerMac.write(&mut input[len..]).unwrap();
         // 00:00:5e:00:53:af as byte array
         let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
-        mac_arr.write(&mut input[len..]).unwrap();
+        len += mac_arr.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "mac: 00:00:5e:00:53:af");
@@ -750,10 +847,12 @@ mod test {
         len += DisplayHint::UpperMac.write(&mut input[len..]).unwrap();
         // 00:00:5E:00:53:AF as byte array
         let mac_arr: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x53, 0xaf];
-        mac_arr.write(&mut input[len..]).unwrap();
+        len += mac_arr.write(&mut input[len..]).unwrap();
+
+        _ = len;
 
         let logger = logger();
-        let _ = log_buf(&input, logger);
+        let () = log_buf(&input, logger).unwrap();
         testing_logger::validate(|captured_logs| {
             assert_eq!(captured_logs.len(), 1);
             assert_eq!(captured_logs[0].body, "mac: 00:00:5E:00:53:AF");

+ 0 - 10
aya-obj/src/relocation.rs

@@ -590,8 +590,6 @@ mod test {
 
         assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
         assert_eq!(fun.instructions[0].imm, 1);
-
-        mem::forget(map);
     }
 
     #[test]
@@ -651,9 +649,6 @@ mod test {
 
         assert_eq!(fun.instructions[1].src_reg(), BPF_PSEUDO_MAP_FD as u8);
         assert_eq!(fun.instructions[1].imm, 2);
-
-        mem::forget(map_1);
-        mem::forget(map_2);
     }
 
     #[test]
@@ -690,8 +685,6 @@ mod test {
 
         assert_eq!(fun.instructions[0].src_reg(), BPF_PSEUDO_MAP_FD as u8);
         assert_eq!(fun.instructions[0].imm, 1);
-
-        mem::forget(map);
     }
 
     #[test]
@@ -751,8 +744,5 @@ mod test {
 
         assert_eq!(fun.instructions[1].src_reg(), BPF_PSEUDO_MAP_FD as u8);
         assert_eq!(fun.instructions[1].imm, 2);
-
-        mem::forget(map_1);
-        mem::forget(map_2);
     }
 }

+ 2 - 0
test/integration-ebpf/src/log.rs

@@ -17,6 +17,8 @@ pub fn test_log(ctx: ProbeContext) {
     trace!(&ctx, "mac lc: {:mac}, mac uc: {:MAC}", mac, mac);
     let hex = 0x2f;
     warn!(&ctx, "hex lc: {:x}, hex uc: {:X}", hex, hex);
+    let hex = [0xde, 0xad, 0xbe, 0xef].as_slice();
+    debug!(&ctx, "hex lc: {:x}, hex uc: {:X}", hex, hex);
 }
 
 #[panic_handler]

+ 6 - 2
test/integration-test/src/tests/log.rs

@@ -104,12 +104,12 @@ async fn log() {
 
     // Call the function that the uprobe is attached to, so it starts logging.
     trigger_ebpf_program();
-    captured_logs.wait_expected_len(5).await;
+    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(), 5);
+    assert_eq!(records.len(), 6);
 
     assert_eq!(records[0].body, "Hello from eBPF!");
     assert_eq!(records[0].level, Level::Debug);
@@ -133,4 +133,8 @@ async fn 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");
 }