expand.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. use proc_macro2::TokenStream;
  2. use quote::quote;
  3. use syn::{
  4. parse::{Parse, ParseStream},
  5. punctuated::Punctuated,
  6. Error, Expr, LitStr, Result, Token,
  7. };
  8. use aya_log_common::DisplayHint;
  9. use aya_log_parser::{parse, Fragment};
  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 ctx = args.ctx;
  65. let target = match args.target {
  66. Some(t) => quote! { #t },
  67. None => quote! { module_path!() },
  68. };
  69. let lvl: TokenStream = if let Some(l) = level {
  70. l
  71. } else if let Some(l) = args.level {
  72. quote! { #l }
  73. } else {
  74. return Err(Error::new(
  75. args.format_string.span(),
  76. "missing `level` argument: try passing an `aya_log_ebpf::Level` value",
  77. ));
  78. };
  79. let format_string = args.format_string;
  80. let format_string_val = format_string.value();
  81. let fragments = parse(&format_string_val).map_err(|e| {
  82. Error::new(
  83. format_string.span(),
  84. format!("could not parse the format string: {e}"),
  85. )
  86. })?;
  87. let mut arg_i = 0;
  88. let mut values = Vec::new();
  89. for fragment in fragments {
  90. match fragment {
  91. Fragment::Literal(s) => values.push(quote!(#s)),
  92. Fragment::Parameter(p) => {
  93. let arg = match args.formatting_args {
  94. Some(ref args) => args[arg_i].clone(),
  95. None => return Err(Error::new(format_string.span(), "no arguments provided")),
  96. };
  97. let (hint, formatter) = match p.hint {
  98. DisplayHint::Default => {
  99. (quote!(DisplayHint::Default), quote!(DefaultFormatter))
  100. }
  101. DisplayHint::LowerHex => {
  102. (quote!(DisplayHint::LowerHex), quote!(LowerHexFormatter))
  103. }
  104. DisplayHint::UpperHex => {
  105. (quote!(DisplayHint::UpperHex), quote!(UpperHexFormatter))
  106. }
  107. DisplayHint::Ip => (quote!(DisplayHint::Ip), quote!(IpFormatter)),
  108. DisplayHint::LowerMac => {
  109. (quote!(DisplayHint::LowerMac), quote!(LowerMacFormatter))
  110. }
  111. DisplayHint::UpperMac => {
  112. (quote!(DisplayHint::UpperMac), quote!(UpperMacFormatter))
  113. }
  114. };
  115. let hint = quote!(::aya_log_ebpf::macro_support::#hint);
  116. let arg = quote!(
  117. {
  118. let tmp = #arg;
  119. let _: &dyn ::aya_log_ebpf::macro_support::#formatter = &tmp;
  120. tmp
  121. }
  122. );
  123. values.push(hint);
  124. values.push(arg);
  125. arg_i += 1;
  126. }
  127. }
  128. }
  129. let num_args = values.len();
  130. let values_iter = values.iter();
  131. Ok(quote! {
  132. {
  133. if let Some(buf_ptr) = unsafe { ::aya_log_ebpf::AYA_LOG_BUF.get_ptr_mut(0) } {
  134. let buf = unsafe { &mut *buf_ptr };
  135. if let Ok(header_len) = ::aya_log_ebpf::write_record_header(
  136. &mut buf.buf,
  137. #target,
  138. #lvl,
  139. module_path!(),
  140. file!(),
  141. line!(),
  142. #num_args,
  143. ) {
  144. let record_len = header_len;
  145. if let Ok(record_len) = {
  146. Ok::<_, ()>(record_len) #( .and_then(|record_len| {
  147. if record_len >= buf.buf.len() {
  148. return Err(());
  149. }
  150. aya_log_ebpf::WriteToBuf::write({ #values_iter }, &mut buf.buf[record_len..]).map(|len| record_len + len)
  151. }) )*
  152. } {
  153. unsafe { ::aya_log_ebpf::AYA_LOGS.output(
  154. #ctx,
  155. &buf.buf[..record_len], 0
  156. )}
  157. }
  158. }
  159. }
  160. }
  161. })
  162. }
  163. pub(crate) fn error(args: LogArgs) -> Result<TokenStream> {
  164. log(
  165. args,
  166. Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }),
  167. )
  168. }
  169. pub(crate) fn warn(args: LogArgs) -> Result<TokenStream> {
  170. log(
  171. args,
  172. Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }),
  173. )
  174. }
  175. pub(crate) fn info(args: LogArgs) -> Result<TokenStream> {
  176. log(
  177. args,
  178. Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }),
  179. )
  180. }
  181. pub(crate) fn debug(args: LogArgs) -> Result<TokenStream> {
  182. log(
  183. args,
  184. Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }),
  185. )
  186. }
  187. pub(crate) fn trace(args: LogArgs) -> Result<TokenStream> {
  188. log(
  189. args,
  190. Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }),
  191. )
  192. }