expand.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. use aya_log_common::DisplayHint;
  2. use aya_log_parser::{Fragment, Parameter, parse};
  3. use proc_macro2::{Ident, Span, TokenStream};
  4. use quote::quote;
  5. use syn::{
  6. Error, Expr, LitStr, Result, Token,
  7. parse::{Parse, ParseStream},
  8. punctuated::Punctuated,
  9. };
  10. pub(crate) struct LogArgs {
  11. pub(crate) ctx: Expr,
  12. pub(crate) target: Option<Expr>,
  13. pub(crate) level: Option<Expr>,
  14. pub(crate) format_string: LitStr,
  15. pub(crate) formatting_args: Option<Punctuated<Expr, Token![,]>>,
  16. }
  17. mod kw {
  18. syn::custom_keyword!(target);
  19. }
  20. impl Parse for LogArgs {
  21. fn parse(input: ParseStream<'_>) -> Result<Self> {
  22. let ctx: Expr = input.parse()?;
  23. input.parse::<Token![,]>()?;
  24. // Parse `target: &str`, which is an optional argument.
  25. let target: Option<Expr> = if input.peek(kw::target) {
  26. input.parse::<kw::target>()?;
  27. input.parse::<Token![:]>()?;
  28. let t: Expr = input.parse()?;
  29. input.parse::<Token![,]>()?;
  30. Some(t)
  31. } else {
  32. None
  33. };
  34. // Check whether the next token is `format_string: &str` (which i
  35. // always provided) or `level` (which is an optional expression).
  36. // If `level` is provided, it comes before `format_string`.
  37. let (level, format_string): (Option<Expr>, LitStr) = if input.peek(LitStr) {
  38. // Only `format_string` is provided.
  39. (None, input.parse()?)
  40. } else {
  41. // Both `level` and `format_string` are provided.
  42. let level: Expr = input.parse()?;
  43. input.parse::<Token![,]>()?;
  44. let format_string: LitStr = input.parse()?;
  45. (Some(level), format_string)
  46. };
  47. // Parse variadic arguments.
  48. let formatting_args: Option<Punctuated<Expr, Token![,]>> = if input.is_empty() {
  49. None
  50. } else {
  51. input.parse::<Token![,]>()?;
  52. Some(Punctuated::parse_terminated(input)?)
  53. };
  54. Ok(Self {
  55. ctx,
  56. target,
  57. level,
  58. format_string,
  59. formatting_args,
  60. })
  61. }
  62. }
  63. pub(crate) fn log(args: LogArgs, level_expr: Option<TokenStream>) -> Result<TokenStream> {
  64. let LogArgs {
  65. ctx,
  66. target,
  67. level,
  68. format_string,
  69. formatting_args,
  70. } = args;
  71. let target = match target {
  72. Some(t) => quote! { #t },
  73. None => quote! { module_path!() },
  74. };
  75. let level_expr = match level_expr {
  76. Some(level_expr) => level_expr,
  77. None => {
  78. let level_expr = level.ok_or(Error::new(
  79. format_string.span(),
  80. "missing `level` argument: try passing an `aya_log_ebpf::Level` value",
  81. ))?;
  82. quote! { #level_expr }
  83. }
  84. };
  85. let format_string_val = format_string.value();
  86. let fragments = parse(&format_string_val).map_err(|e| {
  87. Error::new(
  88. format_string.span(),
  89. format!("could not parse the format string: {e}"),
  90. )
  91. })?;
  92. let mut arg_i = 0;
  93. let mut values = Vec::new();
  94. for fragment in fragments {
  95. match fragment {
  96. Fragment::Literal(s) => values.push(quote!(#s)),
  97. Fragment::Parameter(Parameter { hint }) => {
  98. let arg = match &formatting_args {
  99. Some(args) => &args[arg_i],
  100. None => return Err(Error::new(format_string.span(), "no arguments provided")),
  101. };
  102. let (hint, formatter) = match hint {
  103. DisplayHint::Default => {
  104. (quote!(DisplayHint::Default), quote!(DefaultFormatter))
  105. }
  106. DisplayHint::LowerHex => {
  107. (quote!(DisplayHint::LowerHex), quote!(LowerHexFormatter))
  108. }
  109. DisplayHint::UpperHex => {
  110. (quote!(DisplayHint::UpperHex), quote!(UpperHexFormatter))
  111. }
  112. DisplayHint::Ip => (quote!(DisplayHint::Ip), quote!(IpFormatter)),
  113. DisplayHint::LowerMac => {
  114. (quote!(DisplayHint::LowerMac), quote!(LowerMacFormatter))
  115. }
  116. DisplayHint::UpperMac => {
  117. (quote!(DisplayHint::UpperMac), quote!(UpperMacFormatter))
  118. }
  119. DisplayHint::Pointer => {
  120. (quote!(DisplayHint::Pointer), quote!(PointerFormatter))
  121. }
  122. };
  123. let hint = quote!(::aya_log_ebpf::macro_support::#hint);
  124. let arg = quote!(
  125. {
  126. let tmp = #arg;
  127. let _: &dyn ::aya_log_ebpf::macro_support::#formatter = &tmp;
  128. tmp
  129. }
  130. );
  131. values.push(hint);
  132. values.push(arg);
  133. arg_i += 1;
  134. }
  135. }
  136. }
  137. let idents: Vec<_> = (0..values.len())
  138. .map(|arg_i| quote::format_ident!("__arg{arg_i}"))
  139. .collect();
  140. let num_args = values.len();
  141. let num_args = u32::try_from(num_args).map_err(|core::num::TryFromIntError { .. }| {
  142. Error::new(
  143. Span::call_site(),
  144. format!("too many arguments: {num_args} overflows u32"),
  145. )
  146. })?;
  147. let level = Ident::new("level", Span::mixed_site());
  148. let header = Ident::new("__header", Span::call_site());
  149. let tmp = Ident::new("__tmp", Span::call_site());
  150. let kind = Ident::new("__kind", Span::call_site());
  151. let value = Ident::new("__value", Span::call_site());
  152. let size = Ident::new("__size", Span::call_site());
  153. let capacity = Ident::new("__capacity", Span::call_site());
  154. let pos = Ident::new("__pos", Span::call_site());
  155. let op = Ident::new("__op", Span::call_site());
  156. let buf = Ident::new("__buf", Span::call_site());
  157. Ok(quote! {
  158. {
  159. let #level = #level_expr;
  160. if ::aya_log_ebpf::macro_support::level_enabled(#level) {
  161. // Silence unused variable warning; we may need ctx in the future.
  162. let _ = #ctx;
  163. let _: Option<()> = (|| {
  164. use ::aya_log_ebpf::macro_support::{Header, Field, Argument, AYA_LOGS};
  165. let #header = Header::new(
  166. #target,
  167. #level,
  168. module_path!(),
  169. file!(),
  170. line!(),
  171. #num_args,
  172. )?;
  173. #(
  174. let #tmp = #values;
  175. let (#kind, #value) = #tmp.as_argument();
  176. let #idents = Field::new(#kind, #value)?;
  177. )*
  178. let mut #size = size_of::<::aya_log_ebpf::macro_support::LogValueLength>(); // For the size field itself.
  179. let mut #op = |slice: &[u8]| {
  180. #size += slice.len();
  181. Some(())
  182. };
  183. #header.with_bytes(&mut #op)?;
  184. #(
  185. #idents.with_bytes(&mut #op)?;
  186. )*
  187. let #size = match ::aya_log_ebpf::macro_support::LogValueLength::try_from(#size) {
  188. Ok(#size) => #size,
  189. Err(core::num::TryFromIntError { .. }) => return None,
  190. };
  191. let #size = core::hint::black_box(#size);
  192. let mut #capacity = 64;
  193. while #capacity < #size {
  194. #capacity <<= 1;
  195. if #capacity > 8192 {
  196. // The size is too large to log.
  197. return None;
  198. }
  199. }
  200. let mut #buf = core::hint::black_box(AYA_LOGS.reserve_bytes(#capacity.into(), 0)?);
  201. match (|| {
  202. let mut #pos = 0;
  203. let mut #op = |slice: &[u8]| {
  204. let #buf = #buf.get_mut(#pos..)?;
  205. let #buf = #buf.get_mut(..slice.len())?;
  206. #buf.copy_from_slice(slice);
  207. #pos += slice.len();
  208. Some(())
  209. };
  210. #op(#size.to_ne_bytes().as_ref())?;
  211. #header.with_bytes(&mut #op)?;
  212. #(
  213. #idents.with_bytes(&mut #op)?;
  214. )*
  215. Some(())
  216. })() {
  217. Some(()) => #buf.submit(0),
  218. None => #buf.discard(0),
  219. }
  220. Some(())
  221. })();
  222. }
  223. }
  224. })
  225. }
  226. pub(crate) fn error(args: LogArgs) -> Result<TokenStream> {
  227. log(
  228. args,
  229. Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }),
  230. )
  231. }
  232. pub(crate) fn warn(args: LogArgs) -> Result<TokenStream> {
  233. log(
  234. args,
  235. Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }),
  236. )
  237. }
  238. pub(crate) fn info(args: LogArgs) -> Result<TokenStream> {
  239. log(
  240. args,
  241. Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }),
  242. )
  243. }
  244. pub(crate) fn debug(args: LogArgs) -> Result<TokenStream> {
  245. log(
  246. args,
  247. Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }),
  248. )
  249. }
  250. pub(crate) fn trace(args: LogArgs) -> Result<TokenStream> {
  251. log(
  252. args,
  253. Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }),
  254. )
  255. }