aya_log_common/
lib.rs

1#![no_std]
2
3use core::{
4    net::{IpAddr, Ipv4Addr, Ipv6Addr},
5    num::{NonZeroUsize, TryFromIntError},
6};
7
8use num_enum::IntoPrimitive;
9
10pub const LOG_BUF_CAPACITY: usize = 8192;
11
12pub const LOG_FIELDS: usize = 6;
13
14pub type LogValueLength = u16;
15
16#[repr(u8)]
17#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
18pub enum Level {
19    /// The "error" level.
20    ///
21    /// Designates very serious errors.
22    Error = 1,
23    /// The "warn" level.
24    ///
25    /// Designates hazardous situations.
26    Warn,
27    /// The "info" level.
28    ///
29    /// Designates useful information.
30    Info,
31    /// The "debug" level.
32    ///
33    /// Designates lower priority information.
34    Debug,
35    /// The "trace" level.
36    ///
37    /// Designates very low priority, often extremely verbose, information.
38    Trace,
39}
40
41macro_rules! impl_formatter_for_types {
42    ($trait:path : { $($type:ty),*}) => {
43        $(
44            impl $trait for $type {}
45        )*
46    };
47}
48
49pub trait DefaultFormatter {}
50impl_formatter_for_types!(
51    DefaultFormatter: {
52        bool,
53        i8, i16, i32, i64, isize,
54        u8, u16, u32, u64, usize,
55        f32, f64,
56        char,
57        str,
58        &str,
59        IpAddr, Ipv4Addr, Ipv6Addr
60    }
61);
62
63pub trait LowerHexFormatter {}
64impl_formatter_for_types!(
65    LowerHexFormatter: {
66        i8, i16, i32, i64, isize,
67        u8, u16, u32, u64, usize,
68        &[u8]
69    }
70);
71
72pub trait UpperHexFormatter {}
73impl_formatter_for_types!(
74    UpperHexFormatter: {
75        i8, i16, i32, i64, isize,
76        u8, u16, u32, u64, usize,
77        &[u8]
78    }
79);
80
81pub trait IpFormatter {}
82impl IpFormatter for IpAddr {}
83impl IpFormatter for Ipv4Addr {}
84impl IpFormatter for Ipv6Addr {}
85impl IpFormatter for u32 {}
86impl IpFormatter for [u8; 4] {}
87impl IpFormatter for [u8; 16] {}
88impl IpFormatter for [u16; 8] {}
89
90pub trait LowerMacFormatter {}
91impl LowerMacFormatter for [u8; 6] {}
92
93pub trait UpperMacFormatter {}
94impl UpperMacFormatter for [u8; 6] {}
95
96#[repr(u8)]
97#[derive(Copy, Clone, Debug, IntoPrimitive)]
98pub enum RecordField {
99    Target = 1,
100    Level,
101    Module,
102    File,
103    Line,
104    NumArgs,
105}
106
107/// Types which are supported by aya-log and can be safely sent from eBPF
108/// programs to userspace.
109#[repr(u8)]
110#[derive(Copy, Clone, Debug, IntoPrimitive)]
111pub enum Argument {
112    DisplayHint,
113
114    I8,
115    I16,
116    I32,
117    I64,
118    Isize,
119
120    U8,
121    U16,
122    U32,
123    U64,
124    Usize,
125
126    F32,
127    F64,
128
129    Ipv4Addr,
130    Ipv6Addr,
131
132    /// `[u8; 4]` array which represents an IPv4 address.
133    ArrU8Len4,
134    /// `[u8; 6]` array which represents a MAC address.
135    ArrU8Len6,
136    /// `[u8; 16]` array which represents an IPv6 address.
137    ArrU8Len16,
138    /// `[u16; 8]` array which represents an IPv6 address.
139    ArrU16Len8,
140
141    Bytes,
142    Str,
143}
144
145/// All display hints
146#[repr(u8)]
147#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive)]
148pub enum DisplayHint {
149    /// Default string representation.
150    Default = 1,
151    /// `:x`
152    LowerHex,
153    /// `:X`
154    UpperHex,
155    /// `:i`
156    Ip,
157    /// `:mac`
158    LowerMac,
159    /// `:MAC`
160    UpperMac,
161}
162
163// Must be inlined, else the BPF backend emits:
164//
165// llvm: <unknown>:0:0: in function _ZN14aya_log_common5write17hc9ed05433e23a663E { i64, i64 } (i8, ptr, i64, ptr, i64): only integer returns supported
166#[inline(always)]
167pub(crate) fn write(tag: u8, value: &[u8], buf: &mut [u8]) -> Option<NonZeroUsize> {
168    let wire_len: LogValueLength = match value.len().try_into() {
169        Ok(wire_len) => Some(wire_len),
170        Err(TryFromIntError { .. }) => None,
171    }?;
172    let mut size = 0;
173    macro_rules! copy_from_slice {
174        ($value:expr) => {{
175            let buf = buf.get_mut(size..)?;
176            let buf = buf.get_mut(..$value.len())?;
177            buf.copy_from_slice($value);
178            size += $value.len();
179        }};
180    }
181    copy_from_slice!(&[tag]);
182    copy_from_slice!(&wire_len.to_ne_bytes());
183    copy_from_slice!(value);
184    NonZeroUsize::new(size)
185}
186
187pub trait WriteToBuf {
188    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize>;
189}
190
191macro_rules! impl_write_to_buf {
192    ($type:ident, $arg_type:expr) => {
193        impl WriteToBuf for $type {
194            // This need not be inlined because the return value is Option<N> where N is
195            // mem::size_of<$type>, which is a compile-time constant.
196            #[inline(never)]
197            fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
198                write($arg_type.into(), &self.to_ne_bytes(), buf)
199            }
200        }
201    };
202}
203
204impl_write_to_buf!(i8, Argument::I8);
205impl_write_to_buf!(i16, Argument::I16);
206impl_write_to_buf!(i32, Argument::I32);
207impl_write_to_buf!(i64, Argument::I64);
208impl_write_to_buf!(isize, Argument::Isize);
209
210impl_write_to_buf!(u8, Argument::U8);
211impl_write_to_buf!(u16, Argument::U16);
212impl_write_to_buf!(u32, Argument::U32);
213impl_write_to_buf!(u64, Argument::U64);
214impl_write_to_buf!(usize, Argument::Usize);
215
216impl_write_to_buf!(f32, Argument::F32);
217impl_write_to_buf!(f64, Argument::F64);
218
219impl WriteToBuf for IpAddr {
220    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
221        match self {
222            IpAddr::V4(ipv4_addr) => write(Argument::Ipv4Addr.into(), &ipv4_addr.octets(), buf),
223            IpAddr::V6(ipv6_addr) => write(Argument::Ipv6Addr.into(), &ipv6_addr.octets(), buf),
224        }
225    }
226}
227
228impl WriteToBuf for Ipv4Addr {
229    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
230        write(Argument::Ipv4Addr.into(), &self.octets(), buf)
231    }
232}
233
234impl WriteToBuf for [u8; 4] {
235    // This need not be inlined because the return value is Option<N> where N is 16, which is a
236    // compile-time constant.
237    #[inline(never)]
238    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
239        write(Argument::ArrU8Len4.into(), &self, buf)
240    }
241}
242
243impl WriteToBuf for Ipv6Addr {
244    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
245        write(Argument::Ipv6Addr.into(), &self.octets(), buf)
246    }
247}
248
249impl WriteToBuf for [u8; 16] {
250    // This need not be inlined because the return value is Option<N> where N is 16, which is a
251    // compile-time constant.
252    #[inline(never)]
253    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
254        write(Argument::ArrU8Len16.into(), &self, buf)
255    }
256}
257
258impl WriteToBuf for [u16; 8] {
259    // This need not be inlined because the return value is Option<N> where N is 16, which is a
260    // compile-time constant.
261    #[inline(never)]
262    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
263        let bytes = unsafe { core::mem::transmute::<[u16; 8], [u8; 16]>(self) };
264        write(Argument::ArrU16Len8.into(), &bytes, buf)
265    }
266}
267
268impl WriteToBuf for [u8; 6] {
269    // This need not be inlined because the return value is Option<N> where N is 6, which is a
270    // compile-time constant.
271    #[inline(never)]
272    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
273        write(Argument::ArrU8Len6.into(), &self, buf)
274    }
275}
276
277impl WriteToBuf for &[u8] {
278    // Must be inlined, else the BPF backend emits:
279    //
280    // llvm: <unknown>:0:0: in function _ZN63_$LT$$RF$$u5b$u8$u5d$$u20$as$u20$aya_log_common..WriteToBuf$GT$5write17h08f30a45f7b9f09dE { i64, i64 } (ptr, i64, ptr, i64): only integer returns supported
281    #[inline(always)]
282    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
283        write(Argument::Bytes.into(), self, buf)
284    }
285}
286
287impl WriteToBuf for &str {
288    // Must be inlined, else the BPF backend emits:
289    //
290    // llvm: <unknown>:0:0: in function _ZN54_$LT$$RF$str$u20$as$u20$aya_log_common..WriteToBuf$GT$5write17h7e2d1ccaa758e2b5E { i64, i64 } (ptr, i64, ptr, i64): only integer returns supported
291    #[inline(always)]
292    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
293        write(Argument::Str.into(), self.as_bytes(), buf)
294    }
295}
296
297impl WriteToBuf for DisplayHint {
298    // This need not be inlined because the return value is Option<N> where N is 1, which is a
299    // compile-time constant.
300    #[inline(never)]
301    fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
302        let v: u8 = self.into();
303        write(Argument::DisplayHint.into(), &v.to_ne_bytes(), buf)
304    }
305}
306
307#[doc(hidden)]
308#[inline(always)] // This function takes too many arguments to not be inlined.
309pub fn write_record_header(
310    buf: &mut [u8],
311    target: &str,
312    level: Level,
313    module: &str,
314    file: &str,
315    line: u32,
316    num_args: usize,
317) -> Option<NonZeroUsize> {
318    let level: u8 = level.into();
319    let mut size = 0;
320    macro_rules! write {
321        ($tag:expr, $value:expr) => {{
322            let buf = buf.get_mut(size..)?;
323            let len = write($tag.into(), $value, buf)?;
324            size += len.get();
325        }};
326    }
327    write!(RecordField::Target, target.as_bytes());
328    write!(RecordField::Level, &level.to_ne_bytes());
329    write!(RecordField::Module, module.as_bytes());
330    write!(RecordField::File, file.as_bytes());
331    write!(RecordField::Line, &line.to_ne_bytes());
332    write!(RecordField::NumArgs, &num_args.to_ne_bytes());
333    NonZeroUsize::new(size)
334}
335
336#[cfg(test)]
337mod test {
338    use super::*;
339
340    #[test]
341    fn log_value_length_sufficient() {
342        assert!(
343            LOG_BUF_CAPACITY <= LogValueLength::MAX.into(),
344            "{} > {}",
345            LOG_BUF_CAPACITY,
346            LogValueLength::MAX
347        );
348    }
349}