| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- use aya_log_common::DisplayHint;
- use aya_log_parser::{Fragment, Parameter, parse};
- use proc_macro2::{Ident, Span, TokenStream};
- use quote::quote;
- use syn::{
- Error, Expr, LitStr, Result, Token,
- parse::{Parse, ParseStream},
- punctuated::Punctuated,
- };
- 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_expr: Option<TokenStream>) -> Result<TokenStream> {
- let LogArgs {
- ctx,
- target,
- level,
- format_string,
- formatting_args,
- } = args;
- let target = match target {
- Some(t) => quote! { #t },
- None => quote! { module_path!() },
- };
- let level_expr = match level_expr {
- Some(level_expr) => level_expr,
- None => {
- let level_expr = level.ok_or(Error::new(
- format_string.span(),
- "missing `level` argument: try passing an `aya_log_ebpf::Level` value",
- ))?;
- quote! { #level_expr }
- }
- };
- let format_string_val = format_string.value();
- let fragments = parse(&format_string_val).map_err(|e| {
- Error::new(
- format_string.span(),
- format!("could not parse the format string: {e}"),
- )
- })?;
- let mut arg_i = 0;
- let mut values = Vec::new();
- for fragment in fragments {
- match fragment {
- Fragment::Literal(s) => values.push(quote!(#s)),
- Fragment::Parameter(Parameter { hint }) => {
- let arg = match &formatting_args {
- Some(args) => &args[arg_i],
- None => return Err(Error::new(format_string.span(), "no arguments provided")),
- };
- let (hint, formatter) = match hint {
- DisplayHint::Default => {
- (quote!(DisplayHint::Default), quote!(DefaultFormatter))
- }
- DisplayHint::LowerHex => {
- (quote!(DisplayHint::LowerHex), quote!(LowerHexFormatter))
- }
- DisplayHint::UpperHex => {
- (quote!(DisplayHint::UpperHex), quote!(UpperHexFormatter))
- }
- DisplayHint::Ip => (quote!(DisplayHint::Ip), quote!(IpFormatter)),
- DisplayHint::LowerMac => {
- (quote!(DisplayHint::LowerMac), quote!(LowerMacFormatter))
- }
- DisplayHint::UpperMac => {
- (quote!(DisplayHint::UpperMac), quote!(UpperMacFormatter))
- }
- DisplayHint::Pointer => {
- (quote!(DisplayHint::Pointer), quote!(PointerFormatter))
- }
- };
- let hint = quote!(::aya_log_ebpf::macro_support::#hint);
- let arg = quote!(
- {
- let tmp = #arg;
- let _: &dyn ::aya_log_ebpf::macro_support::#formatter = &tmp;
- tmp
- }
- );
- values.push(hint);
- values.push(arg);
- arg_i += 1;
- }
- }
- }
- let idents: Vec<_> = (0..values.len())
- .map(|arg_i| quote::format_ident!("__arg{arg_i}"))
- .collect();
- let num_args = values.len();
- let num_args = u32::try_from(num_args).map_err(|core::num::TryFromIntError { .. }| {
- Error::new(
- Span::call_site(),
- format!("too many arguments: {num_args} overflows u32"),
- )
- })?;
- let level = Ident::new("level", Span::mixed_site());
- let header = Ident::new("__header", Span::call_site());
- let tmp = Ident::new("__tmp", Span::call_site());
- let kind = Ident::new("__kind", Span::call_site());
- let value = Ident::new("__value", Span::call_site());
- let size = Ident::new("__size", Span::call_site());
- let capacity = Ident::new("__capacity", Span::call_site());
- let pos = Ident::new("__pos", Span::call_site());
- let op = Ident::new("__op", Span::call_site());
- let buf = Ident::new("__buf", Span::call_site());
- Ok(quote! {
- {
- let #level = #level_expr;
- if ::aya_log_ebpf::macro_support::level_enabled(#level) {
- // Silence unused variable warning; we may need ctx in the future.
- let _ = #ctx;
- let _: Option<()> = (|| {
- use ::aya_log_ebpf::macro_support::{Header, Field, Argument, AYA_LOGS};
- let #header = Header::new(
- #target,
- #level,
- module_path!(),
- file!(),
- line!(),
- #num_args,
- )?;
- #(
- let #tmp = #values;
- let (#kind, #value) = #tmp.as_argument();
- let #idents = Field::new(#kind, #value)?;
- )*
- let mut #size = size_of::<::aya_log_ebpf::macro_support::LogValueLength>(); // For the size field itself.
- let mut #op = |slice: &[u8]| {
- #size += slice.len();
- Some(())
- };
- #header.with_bytes(&mut #op)?;
- #(
- #idents.with_bytes(&mut #op)?;
- )*
- let #size = match ::aya_log_ebpf::macro_support::LogValueLength::try_from(#size) {
- Ok(#size) => #size,
- Err(core::num::TryFromIntError { .. }) => return None,
- };
- let #size = core::hint::black_box(#size);
- let mut #capacity = 64;
- while #capacity < #size {
- #capacity <<= 1;
- if #capacity > 8192 {
- // The size is too large to log.
- return None;
- }
- }
- let mut #buf = core::hint::black_box(AYA_LOGS.reserve_bytes(#capacity.into(), 0)?);
- match (|| {
- let mut #pos = 0;
- let mut #op = |slice: &[u8]| {
- let #buf = #buf.get_mut(#pos..)?;
- let #buf = #buf.get_mut(..slice.len())?;
- #buf.copy_from_slice(slice);
- #pos += slice.len();
- Some(())
- };
- #op(#size.to_ne_bytes().as_ref())?;
- #header.with_bytes(&mut #op)?;
- #(
- #idents.with_bytes(&mut #op)?;
- )*
- Some(())
- })() {
- Some(()) => #buf.submit(0),
- None => #buf.discard(0),
- }
- Some(())
- })();
- }
- }
- })
- }
- 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 }),
- )
- }
|