mirror of https://github.com/aya-rs/aya
Format arguments in userspace
This change moves away argument formatting from eBPF to the userspace. eBPF part of aya-log writes unformatted log message and all arguments to the perf buffer and the userspace part of aya-log is formatting the message after receiving all arguments. Aya-based project to test this change: https://github.com/vadorovsky/aya-log-example Fixes: #4 Signed-off-by: Michal Rostecki <vadorovsky@gmail.com> Signed-off-by: Tuetuopay <tuetuopay@me.com> Co-authored-by: Tuetuopay <tuetuopay@me.com>pull/350/head
parent
9b229d00e1
commit
ca1fe7e05f
@ -1,130 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
pub extern crate ufmt;
|
|
||||||
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
use core::{cmp, mem, ptr};
|
|
||||||
|
|
||||||
use aya_bpf::{
|
|
||||||
macros::map,
|
|
||||||
maps::{PerCpuArray, PerfEventByteArray},
|
|
||||||
};
|
|
||||||
pub use aya_log_common::Level;
|
|
||||||
use aya_log_common::RecordField;
|
|
||||||
pub use aya_log_common::LOG_BUF_CAPACITY;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct LogBuf {
|
|
||||||
pub buf: [u8; LOG_BUF_CAPACITY],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[map]
|
|
||||||
pub static mut AYA_LOG_BUF: PerCpuArray<LogBuf> = PerCpuArray::with_max_entries(1, 0);
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[map]
|
|
||||||
pub static mut AYA_LOGS: PerfEventByteArray = PerfEventByteArray::new(0);
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct LogBufWriter<'a> {
|
|
||||||
pos: usize,
|
|
||||||
data: &'a mut [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LogBufWriter<'a> {
|
|
||||||
pub fn new(data: &mut [u8]) -> LogBufWriter<'_> {
|
|
||||||
LogBufWriter {
|
|
||||||
pos: mem::size_of::<RecordField>() + mem::size_of::<usize>(),
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(self) -> usize {
|
|
||||||
let mut buf = self.data;
|
|
||||||
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, RecordField::Log) };
|
|
||||||
buf = &mut buf[mem::size_of::<RecordField>()..];
|
|
||||||
|
|
||||||
let len = self.pos - mem::size_of::<RecordField>() - mem::size_of::<usize>();
|
|
||||||
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, len) };
|
|
||||||
|
|
||||||
self.pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ufmt::uWrite for LogBufWriter<'a> {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
|
|
||||||
let bytes = s.as_bytes();
|
|
||||||
let len = bytes.len();
|
|
||||||
|
|
||||||
// this is to make sure the verifier knows about the upper bound
|
|
||||||
if len > LOG_BUF_CAPACITY {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let available = self.data.len() - self.pos;
|
|
||||||
if available < len {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.data[self.pos..self.pos + len].copy_from_slice(&bytes[..len]);
|
|
||||||
self.pos += len;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TagLenValue<'a> {
|
|
||||||
tag: RecordField,
|
|
||||||
value: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TagLenValue<'a> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn new(tag: RecordField, value: &'a [u8]) -> TagLenValue<'a> {
|
|
||||||
TagLenValue { tag, value }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn try_write(&self, mut buf: &mut [u8]) -> Result<usize, ()> {
|
|
||||||
let size = mem::size_of::<RecordField>() + 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::<RecordField>()..];
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn write_record_header(
|
|
||||||
buf: &mut [u8],
|
|
||||||
target: &str,
|
|
||||||
level: Level,
|
|
||||||
module: &str,
|
|
||||||
file: &str,
|
|
||||||
line: u32,
|
|
||||||
) -> Result<usize, ()> {
|
|
||||||
let mut size = 0;
|
|
||||||
for attr in [
|
|
||||||
TagLenValue::new(RecordField::Target, target.as_bytes()),
|
|
||||||
TagLenValue::new(RecordField::Level, &(level as usize).to_ne_bytes()),
|
|
||||||
TagLenValue::new(RecordField::Module, module.as_bytes()),
|
|
||||||
TagLenValue::new(RecordField::File, file.as_bytes()),
|
|
||||||
TagLenValue::new(RecordField::Line, &line.to_ne_bytes()),
|
|
||||||
] {
|
|
||||||
size += attr.try_write(&mut buf[size..])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(size)
|
|
||||||
}
|
|
@ -1,205 +0,0 @@
|
|||||||
/// Logs a message at the error level.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # let ctx = ();
|
|
||||||
/// # let err_code = -1;
|
|
||||||
/// use aya_log_ebpf::error;
|
|
||||||
///
|
|
||||||
/// error!(&ctx, "Error redirecting packet: {}", err_code);
|
|
||||||
/// error!(&ctx, target: "ingress", "Error redirecting packet: {}", err_code);
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
($ctx:expr, target: $target:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, target: $target, $crate::Level::Error, $($arg)+)
|
|
||||||
);
|
|
||||||
($ctx:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, $crate::Level::Error, $($arg)+)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Logs a message at the warn level.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use aya_log_ebpf::warn;
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// let warn_description = "Invalid Input";
|
|
||||||
///
|
|
||||||
/// warn!("Warning! {}!", warn_description);
|
|
||||||
/// warn!(target: "input_events", "App received warning: {}", warn_description);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn {
|
|
||||||
($ctx:expr, target: $target:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, target: $target, $crate::Level::Warn, $($arg)+)
|
|
||||||
);
|
|
||||||
($ctx:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, $crate::Level::Warn, $($arg)+)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Logs a message at the info level.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```edition2018
|
|
||||||
/// use log::info;
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// # struct Connection { port: u32, speed: u32 }
|
|
||||||
/// let conn_info = Connection { port: 40, speed: 3 };
|
|
||||||
///
|
|
||||||
/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed);
|
|
||||||
/// info!(target: "connection_events", "Successfull connection, port: {}, speed: {}",
|
|
||||||
/// conn_info.port, conn_info.speed);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
($ctx:expr, target: $target:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, target: $target, $crate::Level::Info, $($arg)+)
|
|
||||||
);
|
|
||||||
($ctx:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, $crate::Level::Info, $($arg)+)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Logs a message at the debug level.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```edition2018
|
|
||||||
/// use log::debug;
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// # struct Position { x: i64, y: i64 }
|
|
||||||
/// let pos = Position { x: 3.234, y: -1223 };
|
|
||||||
///
|
|
||||||
/// debug!("New position: x: {}, y: {}", pos.x, pos.y);
|
|
||||||
/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! debug {
|
|
||||||
($ctx:expr, target: $target:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, target: $target, $crate::Level::Debug, $($arg)+)
|
|
||||||
);
|
|
||||||
($ctx:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, $crate::Level::Debug, $($arg)+)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Logs a message at the trace level.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```edition2018
|
|
||||||
/// use log::trace;
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// # struct Position { x: i64, y: i64 }
|
|
||||||
/// let pos = Position { x: 3234, y: -1223 };
|
|
||||||
///
|
|
||||||
/// trace!("Position is: x: {}, y: {}", pos.x, pos.y);
|
|
||||||
/// trace!(target: "app_events", "x is {} and y is {}",
|
|
||||||
/// if pos.x >= 0 { "positive" } else { "negative" },
|
|
||||||
/// if pos.y >= 0 { "positive" } else { "negative" });
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! trace {
|
|
||||||
($ctx:expr, target: $target:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, target: $target, $crate::Level::Trace, $($arg)+)
|
|
||||||
);
|
|
||||||
($ctx:expr, $($arg:tt)+) => (
|
|
||||||
$crate::log!($ctx, $crate::Level::Trace, $($arg)+)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// /// Determines if a message logged at the specified level in that module will
|
|
||||||
// /// be logged.
|
|
||||||
// ///
|
|
||||||
// /// This can be used to avoid expensive computation of log message arguments if
|
|
||||||
// /// the message would be ignored anyway.
|
|
||||||
// ///
|
|
||||||
// /// # Examples
|
|
||||||
// ///
|
|
||||||
// /// ```edition2018
|
|
||||||
// /// use log::Level::Debug;
|
|
||||||
// /// use log::{debug, log_enabled};
|
|
||||||
// ///
|
|
||||||
// /// # fn foo() {
|
|
||||||
// /// if log_enabled!(Debug) {
|
|
||||||
// /// let data = expensive_call();
|
|
||||||
// /// debug!("expensive debug data: {} {}", data.x, data.y);
|
|
||||||
// /// }
|
|
||||||
// /// if log_enabled!(target: "Global", Debug) {
|
|
||||||
// /// let data = expensive_call();
|
|
||||||
// /// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y);
|
|
||||||
// /// }
|
|
||||||
// /// # }
|
|
||||||
// /// # struct Data { x: u32, y: u32 }
|
|
||||||
// /// # fn expensive_call() -> Data { Data { x: 0, y: 0 } }
|
|
||||||
// /// # fn main() {}
|
|
||||||
// /// ```
|
|
||||||
// macro_rules! log_enabled {
|
|
||||||
// (target: $target:expr, $lvl:expr) => {{
|
|
||||||
// let lvl = $lvl;
|
|
||||||
// lvl <= $crate::STATIC_MAX_LEVEL
|
|
||||||
// }};
|
|
||||||
// ($lvl:expr) => {
|
|
||||||
// log_enabled!(target: __log_module_path!(), $lvl)
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Log a message at the given level.
|
|
||||||
///
|
|
||||||
/// This macro will generically log with the specified `Level` and `format!`
|
|
||||||
/// based argument list.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```edition2018
|
|
||||||
/// use log::{log, Level};
|
|
||||||
///
|
|
||||||
/// # fn main() {
|
|
||||||
/// let data = (42, "Forty-two");
|
|
||||||
/// let private_data = "private";
|
|
||||||
///
|
|
||||||
/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1);
|
|
||||||
/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}",
|
|
||||||
/// data.0, data.1, private_data);
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! log {
|
|
||||||
($ctx:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
|
|
||||||
if let Some(buf) = unsafe { $crate::AYA_LOG_BUF.get_mut(0) } {
|
|
||||||
if let Ok(header_len) = $crate::write_record_header(&mut buf.buf, module_path!(), $lvl, module_path!(), file!(), line!()) {
|
|
||||||
if let Ok(message_len) = $crate::write_record_message!(&mut buf.buf[header_len..], $($arg)+) {
|
|
||||||
let record_len = header_len + message_len;
|
|
||||||
if record_len <= $crate::LOG_BUF_CAPACITY {
|
|
||||||
let _ = unsafe { $crate::AYA_LOGS.output($ctx, &buf.buf[..record_len], 0) };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
($ctx:expr, $lvl:expr, $($arg:tt)+) => ($crate::log!($ctx, target: __log_module_path!(), $lvl, $($arg)+))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! write_record_message {
|
|
||||||
($buf:expr, $($arg:tt)+) => {{
|
|
||||||
use aya_log_ebpf::ufmt;
|
|
||||||
let mut writer = $crate::LogBufWriter::new($buf);
|
|
||||||
aya_log_ebpf::ufmt::uwrite!(writer, $($arg)+).map(|_| writer.finish())
|
|
||||||
}}
|
|
||||||
}
|
|
@ -0,0 +1,2 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["aya-log-ebpf", "aya-log-ebpf-macros"]
|
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "aya-log-ebpf-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0"
|
||||||
|
quote = "1.0"
|
||||||
|
syn = "1.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
@ -0,0 +1,185 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
punctuated::Punctuated,
|
||||||
|
Error, Expr, LitStr, Result, Token,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct LogArgs {
|
||||||
|
pub(crate) ctx: Expr,
|
||||||
|
pub(crate) target: Option<Expr>,
|
||||||
|
pub(crate) level: Option<Expr>,
|
||||||
|
pub(crate) format_string: LitStr,
|
||||||
|
pub(crate) formatting_args: Option<Punctuated<Expr, Token![,]>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod kw {
|
||||||
|
syn::custom_keyword!(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for LogArgs {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let ctx: Expr = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
|
||||||
|
// Parse `target: &str`, which is an optional argument.
|
||||||
|
let target: Option<Expr> = if input.peek(kw::target) {
|
||||||
|
input.parse::<kw::target>()?;
|
||||||
|
input.parse::<Token![:]>()?;
|
||||||
|
let t: Expr = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
Some(t)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check whether the next token is `format_string: &str` (which i
|
||||||
|
// always provided) or `level` (which is an optional expression).
|
||||||
|
// If `level` is provided, it comes before `format_string`.
|
||||||
|
let (level, format_string): (Option<Expr>, LitStr) = if input.peek(LitStr) {
|
||||||
|
// Only `format_string` is provided.
|
||||||
|
(None, input.parse()?)
|
||||||
|
} else {
|
||||||
|
// Both `level` and `format_string` are provided.
|
||||||
|
let level: Expr = input.parse()?;
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
let format_string: LitStr = input.parse()?;
|
||||||
|
(Some(level), format_string)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse variadic arguments.
|
||||||
|
let formatting_args: Option<Punctuated<Expr, Token![,]>> = if input.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
Some(Punctuated::parse_terminated(input)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
ctx,
|
||||||
|
target,
|
||||||
|
level,
|
||||||
|
format_string,
|
||||||
|
formatting_args,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStream> {
|
||||||
|
let ctx = args.ctx;
|
||||||
|
let target = match args.target {
|
||||||
|
Some(t) => quote! { #t },
|
||||||
|
None => quote! { module_path!() },
|
||||||
|
};
|
||||||
|
let lvl: TokenStream = if let Some(l) = level {
|
||||||
|
l
|
||||||
|
} else if let Some(l) = args.level {
|
||||||
|
quote! { #l }
|
||||||
|
} else {
|
||||||
|
return Err(Error::new(
|
||||||
|
args.format_string.span(),
|
||||||
|
"missing `level` argument: try passing an `aya_log_ebpf::Level` value",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let format_string = args.format_string;
|
||||||
|
|
||||||
|
let (num_args, write_args) = match args.formatting_args {
|
||||||
|
Some(formatting_args) => {
|
||||||
|
let formatting_exprs = formatting_args.iter();
|
||||||
|
let num_args = formatting_exprs.len();
|
||||||
|
|
||||||
|
let write_args = quote! {{
|
||||||
|
use ::aya_log_ebpf::WriteToBuf;
|
||||||
|
Ok::<_, ()>(record_len) #( .and_then(|record_len| {
|
||||||
|
{ #formatting_exprs }.write(&mut buf.buf[record_len..]).map(|len| record_len + len)
|
||||||
|
}) )*
|
||||||
|
}};
|
||||||
|
|
||||||
|
(num_args, write_args)
|
||||||
|
}
|
||||||
|
None => (0, quote! {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// The way of writing to the perf buffer is different depending on whether
|
||||||
|
// we have variadic arguments or not.
|
||||||
|
let write_to_perf_buffer = if num_args > 0 {
|
||||||
|
// Writing with variadic arguments.
|
||||||
|
quote! {
|
||||||
|
if let Ok(record_len) = #write_args {
|
||||||
|
unsafe { ::aya_log_ebpf::AYA_LOGS.output(
|
||||||
|
#ctx,
|
||||||
|
&buf.buf[..record_len], 0
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Writing with no variadic arguments.
|
||||||
|
quote! {
|
||||||
|
unsafe { ::aya_log_ebpf::AYA_LOGS.output(
|
||||||
|
#ctx,
|
||||||
|
&buf.buf[..record_len], 0
|
||||||
|
)}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
{
|
||||||
|
if let Some(buf) = unsafe { ::aya_log_ebpf::AYA_LOG_BUF.get_mut(0) } {
|
||||||
|
if let Ok(header_len) = ::aya_log_ebpf::write_record_header(
|
||||||
|
&mut buf.buf,
|
||||||
|
#target,
|
||||||
|
#lvl,
|
||||||
|
module_path!(),
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
#num_args,
|
||||||
|
) {
|
||||||
|
if let Ok(message_len) = ::aya_log_ebpf::write_record_message(
|
||||||
|
&mut buf.buf[header_len..],
|
||||||
|
#format_string,
|
||||||
|
) {
|
||||||
|
let record_len = header_len + message_len;
|
||||||
|
|
||||||
|
#write_to_perf_buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn error(args: LogArgs) -> Result<TokenStream> {
|
||||||
|
log(
|
||||||
|
args,
|
||||||
|
Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn warn(args: LogArgs) -> Result<TokenStream> {
|
||||||
|
log(
|
||||||
|
args,
|
||||||
|
Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn info(args: LogArgs) -> Result<TokenStream> {
|
||||||
|
log(
|
||||||
|
args,
|
||||||
|
Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug(args: LogArgs) -> Result<TokenStream> {
|
||||||
|
log(
|
||||||
|
args,
|
||||||
|
Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn trace(args: LogArgs) -> Result<TokenStream> {
|
||||||
|
log(
|
||||||
|
args,
|
||||||
|
Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use syn::parse_macro_input;
|
||||||
|
|
||||||
|
mod expand;
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn log(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::log(args, None)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn error(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::error(args)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn warn(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::warn(args)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn info(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::info(args)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn debug(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::debug(args)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn trace(args: TokenStream) -> TokenStream {
|
||||||
|
let args = parse_macro_input!(args as expand::LogArgs);
|
||||||
|
expand::trace(args)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
#![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_ebpf_macros::{debug, error, info, log, trace, warn};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LogBuf {
|
||||||
|
pub buf: [u8; LOG_BUF_CAPACITY],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[map]
|
||||||
|
pub static mut AYA_LOG_BUF: PerCpuArray<LogBuf> = PerCpuArray::with_max_entries(1, 0);
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[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)]
|
||||||
|
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};
|
||||||
|
pub use aya_log_ebpf_macros::log;
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
Loading…
Reference in New Issue