Skip to main content

profile_bee_aya_log_common/
lib.rs

1#![no_std]
2
3use core::{
4    net::{IpAddr, Ipv4Addr, Ipv6Addr},
5    num::TryFromIntError,
6};
7
8use num_enum::IntoPrimitive;
9
10pub type LogValueLength = u16;
11
12#[repr(u8)]
13#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
14pub enum Level {
15    /// The "error" level.
16    ///
17    /// Designates very serious errors.
18    Error = 1,
19    /// The "warn" level.
20    ///
21    /// Designates hazardous situations.
22    Warn,
23    /// The "info" level.
24    ///
25    /// Designates useful information.
26    Info,
27    /// The "debug" level.
28    ///
29    /// Designates lower priority information.
30    Debug,
31    /// The "trace" level.
32    ///
33    /// Designates very low priority, often extremely verbose, information.
34    Trace,
35}
36
37macro_rules! impl_formatter_for_types {
38    ($trait:path : { $($type:ty),*}) => {
39        $(
40            impl $trait for $type {}
41        )*
42    };
43}
44
45pub trait DefaultFormatter {}
46impl_formatter_for_types!(
47    DefaultFormatter: {
48        bool,
49        i8, i16, i32, i64, isize,
50        u8, u16, u32, u64, usize,
51        f32, f64,
52        char,
53        str,
54        &str,
55        IpAddr, Ipv4Addr, Ipv6Addr
56    }
57);
58
59pub trait LowerHexFormatter {}
60impl_formatter_for_types!(
61    LowerHexFormatter: {
62        i8, i16, i32, i64, isize,
63        u8, u16, u32, u64, usize,
64        &[u8]
65    }
66);
67
68impl<const N: usize> LowerHexFormatter for &[u8; N] {}
69
70pub trait UpperHexFormatter {}
71impl_formatter_for_types!(
72    UpperHexFormatter: {
73        i8, i16, i32, i64, isize,
74        u8, u16, u32, u64, usize,
75        &[u8]
76    }
77);
78
79impl<const N: usize> UpperHexFormatter for &[u8; N] {}
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
96pub trait PointerFormatter {}
97impl<T> PointerFormatter for *const T {}
98impl<T> PointerFormatter for *mut T {}
99
100#[repr(u8)]
101#[derive(Copy, Clone, Debug, IntoPrimitive)]
102pub enum RecordFieldKind {
103    Target,
104    Level,
105    Module,
106    File,
107    Line,
108    NumArgs,
109}
110
111/// Types which are supported by aya-log and can be safely sent from eBPF
112/// programs to userspace.
113#[repr(u8)]
114#[derive(Copy, Clone, Debug, IntoPrimitive)]
115pub enum ArgumentKind {
116    DisplayHint,
117
118    I8,
119    I16,
120    I32,
121    I64,
122    Isize,
123
124    U8,
125    U16,
126    U32,
127    U64,
128    Usize,
129
130    F32,
131    F64,
132
133    Ipv4Addr,
134    Ipv6Addr,
135
136    /// `[u8; 4]` array which represents an IPv4 address.
137    ArrU8Len4,
138    /// `[u8; 6]` array which represents a MAC address.
139    ArrU8Len6,
140    /// `[u8; 16]` array which represents an IPv6 address.
141    ArrU8Len16,
142    /// `[u16; 8]` array which represents an IPv6 address.
143    ArrU16Len8,
144
145    Bytes,
146    Str,
147
148    Pointer,
149}
150
151/// All display hints
152#[repr(u8)]
153#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive)]
154pub enum DisplayHint {
155    /// Default string representation.
156    Default = 1,
157    /// `:x`
158    LowerHex,
159    /// `:X`
160    UpperHex,
161    /// `:i`
162    Ip,
163    /// `:mac`
164    LowerMac,
165    /// `:MAC`
166    UpperMac,
167    /// `:p`
168    Pointer,
169}
170
171mod sealed {
172    #[expect(unnameable_types, reason = "this is the sealed trait pattern")]
173    pub trait Sealed {}
174}
175
176pub trait Argument: sealed::Sealed {
177    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>);
178}
179
180macro_rules! impl_argument {
181    ($self:ident, $arg_type:expr) => {
182        impl sealed::Sealed for $self {}
183
184        impl Argument for $self {
185            fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
186                ($arg_type, self.to_ne_bytes())
187            }
188        }
189    };
190}
191
192impl_argument!(i8, ArgumentKind::I8);
193impl_argument!(i16, ArgumentKind::I16);
194impl_argument!(i32, ArgumentKind::I32);
195impl_argument!(i64, ArgumentKind::I64);
196impl_argument!(isize, ArgumentKind::Isize);
197
198impl_argument!(u8, ArgumentKind::U8);
199impl_argument!(u16, ArgumentKind::U16);
200impl_argument!(u32, ArgumentKind::U32);
201impl_argument!(u64, ArgumentKind::U64);
202impl_argument!(usize, ArgumentKind::Usize);
203
204impl_argument!(f32, ArgumentKind::F32);
205impl_argument!(f64, ArgumentKind::F64);
206
207enum Either<L, R> {
208    Left(L),
209    Right(R),
210}
211
212impl<L, R> AsRef<[u8]> for Either<L, R>
213where
214    L: AsRef<[u8]>,
215    R: AsRef<[u8]>,
216{
217    fn as_ref(&self) -> &[u8] {
218        match self {
219            Self::Left(l) => l.as_ref(),
220            Self::Right(r) => r.as_ref(),
221        }
222    }
223}
224
225impl sealed::Sealed for IpAddr {}
226impl Argument for IpAddr {
227    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
228        match self {
229            Self::V4(ipv4_addr) => {
230                let (kind, value) = ipv4_addr.as_argument();
231                (kind, Either::Left(value))
232            }
233            Self::V6(ipv6_addr) => {
234                let (kind, value) = ipv6_addr.as_argument();
235                (kind, Either::Right(value))
236            }
237        }
238    }
239}
240
241impl sealed::Sealed for Ipv4Addr {}
242impl Argument for Ipv4Addr {
243    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
244        (ArgumentKind::Ipv4Addr, self.octets())
245    }
246}
247
248impl sealed::Sealed for Ipv6Addr {}
249impl Argument for Ipv6Addr {
250    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
251        (ArgumentKind::Ipv6Addr, self.octets())
252    }
253}
254
255impl<const N: usize> sealed::Sealed for [u8; N] {}
256impl<const N: usize> Argument for [u8; N] {
257    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
258        let kind = match N {
259            4 => ArgumentKind::ArrU8Len4,
260            6 => ArgumentKind::ArrU8Len6,
261            16 => ArgumentKind::ArrU8Len16,
262            _ => ArgumentKind::Bytes,
263        };
264        (kind, *self)
265    }
266}
267
268impl sealed::Sealed for &[u8] {}
269impl Argument for &[u8] {
270    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
271        (ArgumentKind::Bytes, *self)
272    }
273}
274
275impl sealed::Sealed for &str {}
276impl Argument for &str {
277    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
278        (ArgumentKind::Str, self.as_bytes())
279    }
280}
281
282impl sealed::Sealed for DisplayHint {}
283impl Argument for DisplayHint {
284    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
285        let v: u8 = (*self).into();
286        (ArgumentKind::DisplayHint, v.to_ne_bytes())
287    }
288}
289
290impl<T> sealed::Sealed for *const T {}
291impl<T> Argument for *const T {
292    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
293        (ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
294    }
295}
296
297impl<T> sealed::Sealed for *mut T {}
298impl<T> Argument for *mut T {
299    fn as_argument(&self) -> (ArgumentKind, impl AsRef<[u8]>) {
300        (ArgumentKind::Pointer, (*self as usize).to_ne_bytes())
301    }
302}
303
304fn wire_len(value: &[u8]) -> Option<[u8; 2]> {
305    match LogValueLength::try_from(value.len()) {
306        Ok(wire_len) => Some(wire_len.to_ne_bytes()),
307        Err(TryFromIntError { .. }) => None,
308    }
309}
310
311#[doc(hidden)]
312pub struct Field<T>([u8; 1], [u8; 2], T);
313
314impl<T: AsRef<[u8]>> Field<T> {
315    pub fn new(kind: impl Into<u8>, value: T) -> Option<Self> {
316        let wire_len = wire_len(value.as_ref())?;
317        Some(Self([kind.into()], wire_len, value))
318    }
319
320    pub fn with_bytes(&self, op: &mut impl FnMut(&[u8]) -> Option<()>) -> Option<()> {
321        let Self(kind, wire_len, value) = self;
322        op(&kind[..])?;
323        op(&wire_len[..])?;
324        op(value.as_ref())?;
325        Some(())
326    }
327}
328
329#[doc(hidden)]
330pub struct Header<'a> {
331    target: Field<&'a [u8]>,
332    level: Field<[u8; 1]>,
333    module: Field<&'a [u8]>,
334    file: Field<&'a [u8]>,
335    line: Field<[u8; 4]>,
336    num_args: Field<[u8; 4]>,
337}
338
339impl<'a> Header<'a> {
340    pub fn new(
341        target: &'a str,
342        level: Level,
343        module: &'a str,
344        file: &'a str,
345        line: u32,
346        num_args: u32,
347    ) -> Option<Self> {
348        let target = target.as_bytes();
349        let level: u8 = level.into();
350        let level = level.to_ne_bytes();
351        let module = module.as_bytes();
352        let file = file.as_bytes();
353        let line = line.to_ne_bytes();
354        let num_args = num_args.to_ne_bytes();
355        let target = Field::new(RecordFieldKind::Target, target)?;
356        let level = Field::new(RecordFieldKind::Level, level)?;
357        let module = Field::new(RecordFieldKind::Module, module)?;
358        let file = Field::new(RecordFieldKind::File, file)?;
359        let line = Field::new(RecordFieldKind::Line, line)?;
360        let num_args = Field::new(RecordFieldKind::NumArgs, num_args)?;
361
362        Some(Self {
363            target,
364            level,
365            module,
366            file,
367            line,
368            num_args,
369        })
370    }
371
372    pub fn with_bytes(&self, op: &mut impl FnMut(&[u8]) -> Option<()>) -> Option<()> {
373        let Self {
374            target,
375            level,
376            module,
377            file,
378            line,
379            num_args,
380        } = self;
381        target.with_bytes(op)?;
382        level.with_bytes(op)?;
383        module.with_bytes(op)?;
384        file.with_bytes(op)?;
385        line.with_bytes(op)?;
386        num_args.with_bytes(op)?;
387        Some(())
388    }
389}