4
0

expand.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. use aya_log_common::DisplayHint;
  2. use aya_log_parser::{Fragment, 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: Option<TokenStream>) -> Result<TokenStream> {
  64. let LogArgs {
  65. ctx,
  66. target,
  67. level: level_expr,
  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 = match level {
  76. Some(l) => l,
  77. None => {
  78. let l = level_expr.ok_or(Error::new(
  79. format_string.span(),
  80. "missing `level` argument: try passing an `aya_log_ebpf::Level` value",
  81. ))?;
  82. quote! { #l }
  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(p) => {
  98. let arg = match formatting_args {
  99. Some(ref args) => args[arg_i].clone(),
  100. None => return Err(Error::new(format_string.span(), "no arguments provided")),
  101. };
  102. let (hint, formatter) = match p.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. };
  120. let hint = quote!(::aya_log_ebpf::macro_support::#hint);
  121. let arg = quote!(
  122. {
  123. let tmp = #arg;
  124. let _: &dyn ::aya_log_ebpf::macro_support::#formatter = &tmp;
  125. tmp
  126. }
  127. );
  128. values.push(hint);
  129. values.push(arg);
  130. arg_i += 1;
  131. }
  132. }
  133. }
  134. let num_args = values.len();
  135. let values_iter = values.iter();
  136. let buf = Ident::new("buf", Span::mixed_site());
  137. let size = Ident::new("size", Span::mixed_site());
  138. let len = Ident::new("len", Span::mixed_site());
  139. let record = Ident::new("record", Span::mixed_site());
  140. Ok(quote! {
  141. match ::aya_log_ebpf::macro_support::AYA_LOG_BUF.get_ptr_mut(0).and_then(|ptr| unsafe { ptr.as_mut() }) {
  142. None => {},
  143. Some(::aya_log_ebpf::macro_support::LogBuf { buf: #buf }) => {
  144. // Silence unused variable warning; we may need ctx in the future.
  145. let _ = #ctx;
  146. let _: Option<()> = (|| {
  147. let #size = ::aya_log_ebpf::macro_support::write_record_header(
  148. #buf,
  149. #target,
  150. #level,
  151. module_path!(),
  152. file!(),
  153. line!(),
  154. #num_args,
  155. )?;
  156. let mut #size = #size.get();
  157. #(
  158. {
  159. let #buf = #buf.get_mut(#size..)?;
  160. let #len = ::aya_log_ebpf::macro_support::WriteToBuf::write(#values_iter, #buf)?;
  161. #size += #len.get();
  162. }
  163. )*
  164. let #record = #buf.get(..#size)?;
  165. Result::<_, i64>::ok(::aya_log_ebpf::macro_support::AYA_LOGS.output(#record, 0))
  166. })();
  167. }
  168. }
  169. })
  170. }
  171. pub(crate) fn error(args: LogArgs) -> Result<TokenStream> {
  172. log(
  173. args,
  174. Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }),
  175. )
  176. }
  177. pub(crate) fn warn(args: LogArgs) -> Result<TokenStream> {
  178. log(
  179. args,
  180. Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }),
  181. )
  182. }
  183. pub(crate) fn info(args: LogArgs) -> Result<TokenStream> {
  184. log(
  185. args,
  186. Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }),
  187. )
  188. }
  189. pub(crate) fn debug(args: LogArgs) -> Result<TokenStream> {
  190. log(
  191. args,
  192. Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }),
  193. )
  194. }
  195. pub(crate) fn trace(args: LogArgs) -> Result<TokenStream> {
  196. log(
  197. args,
  198. Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }),
  199. )
  200. }