expand.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. use proc_macro2::TokenStream;
  2. use quote::quote;
  3. use syn::{
  4. parse::{Parse, ParseStream},
  5. parse_str,
  6. punctuated::Punctuated,
  7. Error, Expr, LitStr, Result, Token,
  8. };
  9. use aya_log_common::DisplayHint;
  10. use aya_log_parser::{parse, Fragment};
  11. pub(crate) struct LogArgs {
  12. pub(crate) ctx: Expr,
  13. pub(crate) target: Option<Expr>,
  14. pub(crate) level: Option<Expr>,
  15. pub(crate) format_string: LitStr,
  16. pub(crate) formatting_args: Option<Punctuated<Expr, Token![,]>>,
  17. }
  18. mod kw {
  19. syn::custom_keyword!(target);
  20. }
  21. impl Parse for LogArgs {
  22. fn parse(input: ParseStream) -> Result<Self> {
  23. let ctx: Expr = input.parse()?;
  24. input.parse::<Token![,]>()?;
  25. // Parse `target: &str`, which is an optional argument.
  26. let target: Option<Expr> = if input.peek(kw::target) {
  27. input.parse::<kw::target>()?;
  28. input.parse::<Token![:]>()?;
  29. let t: Expr = input.parse()?;
  30. input.parse::<Token![,]>()?;
  31. Some(t)
  32. } else {
  33. None
  34. };
  35. // Check whether the next token is `format_string: &str` (which i
  36. // always provided) or `level` (which is an optional expression).
  37. // If `level` is provided, it comes before `format_string`.
  38. let (level, format_string): (Option<Expr>, LitStr) = if input.peek(LitStr) {
  39. // Only `format_string` is provided.
  40. (None, input.parse()?)
  41. } else {
  42. // Both `level` and `format_string` are provided.
  43. let level: Expr = input.parse()?;
  44. input.parse::<Token![,]>()?;
  45. let format_string: LitStr = input.parse()?;
  46. (Some(level), format_string)
  47. };
  48. // Parse variadic arguments.
  49. let formatting_args: Option<Punctuated<Expr, Token![,]>> = if input.is_empty() {
  50. None
  51. } else {
  52. input.parse::<Token![,]>()?;
  53. Some(Punctuated::parse_terminated(input)?)
  54. };
  55. Ok(Self {
  56. ctx,
  57. target,
  58. level,
  59. format_string,
  60. formatting_args,
  61. })
  62. }
  63. }
  64. fn string_to_expr(s: String) -> Result<Expr> {
  65. parse_str(&format!("\"{s}\""))
  66. }
  67. fn hint_to_expr(hint: DisplayHint) -> Result<Expr> {
  68. match hint {
  69. DisplayHint::Default => parse_str("::aya_log_ebpf::macro_support::DisplayHint::Default"),
  70. DisplayHint::LowerHex => parse_str("::aya_log_ebpf::macro_support::DisplayHint::LowerHex"),
  71. DisplayHint::UpperHex => parse_str("::aya_log_ebpf::macro_support::DisplayHint::UpperHex"),
  72. DisplayHint::Ipv4 => parse_str("::aya_log_ebpf::macro_support::DisplayHint::Ipv4"),
  73. DisplayHint::Ipv6 => parse_str("::aya_log_ebpf::macro_support::DisplayHint::Ipv6"),
  74. DisplayHint::LowerMac => parse_str("::aya_log_ebpf::macro_support::DisplayHint::LowerMac"),
  75. DisplayHint::UpperMac => parse_str("::aya_log_ebpf::macro_support::DisplayHint::UpperMac"),
  76. }
  77. }
  78. pub(crate) fn log(args: LogArgs, level: Option<TokenStream>) -> Result<TokenStream> {
  79. let ctx = args.ctx;
  80. let target = match args.target {
  81. Some(t) => quote! { #t },
  82. None => quote! { module_path!() },
  83. };
  84. let lvl: TokenStream = if let Some(l) = level {
  85. l
  86. } else if let Some(l) = args.level {
  87. quote! { #l }
  88. } else {
  89. return Err(Error::new(
  90. args.format_string.span(),
  91. "missing `level` argument: try passing an `aya_log_ebpf::Level` value",
  92. ));
  93. };
  94. let format_string = args.format_string;
  95. let format_string_val = format_string.value();
  96. let fragments = parse(&format_string_val).map_err(|e| {
  97. Error::new(
  98. format_string.span(),
  99. format!("could not parse the format string: {e}"),
  100. )
  101. })?;
  102. let mut arg_i = 0;
  103. let mut values = Vec::new();
  104. for fragment in fragments {
  105. match fragment {
  106. Fragment::Literal(s) => {
  107. values.push(string_to_expr(s)?);
  108. }
  109. Fragment::Parameter(p) => {
  110. let arg = match args.formatting_args {
  111. Some(ref args) => args[arg_i].clone(),
  112. None => return Err(Error::new(format_string.span(), "no arguments provided")),
  113. };
  114. values.push(hint_to_expr(p.hint)?);
  115. values.push(arg);
  116. arg_i += 1;
  117. }
  118. }
  119. }
  120. let num_args = values.len();
  121. let values_iter = values.iter();
  122. Ok(quote! {
  123. {
  124. if let Some(buf_ptr) = unsafe { ::aya_log_ebpf::AYA_LOG_BUF.get_ptr_mut(0) } {
  125. let buf = unsafe { &mut *buf_ptr };
  126. if let Ok(header_len) = ::aya_log_ebpf::write_record_header(
  127. &mut buf.buf,
  128. #target,
  129. #lvl,
  130. module_path!(),
  131. file!(),
  132. line!(),
  133. #num_args,
  134. ) {
  135. let record_len = header_len;
  136. if let Ok(record_len) = {
  137. use ::aya_log_ebpf::WriteToBuf;
  138. Ok::<_, ()>(record_len) #( .and_then(|record_len| {
  139. if record_len >= buf.buf.len() {
  140. return Err(());
  141. }
  142. { #values_iter }.write(&mut buf.buf[record_len..]).map(|len| record_len + len)
  143. }) )*
  144. } {
  145. unsafe { ::aya_log_ebpf::AYA_LOGS.output(
  146. #ctx,
  147. &buf.buf[..record_len], 0
  148. )}
  149. }
  150. }
  151. }
  152. }
  153. })
  154. }
  155. pub(crate) fn error(args: LogArgs) -> Result<TokenStream> {
  156. log(
  157. args,
  158. Some(quote! { ::aya_log_ebpf::macro_support::Level::Error }),
  159. )
  160. }
  161. pub(crate) fn warn(args: LogArgs) -> Result<TokenStream> {
  162. log(
  163. args,
  164. Some(quote! { ::aya_log_ebpf::macro_support::Level::Warn }),
  165. )
  166. }
  167. pub(crate) fn info(args: LogArgs) -> Result<TokenStream> {
  168. log(
  169. args,
  170. Some(quote! { ::aya_log_ebpf::macro_support::Level::Info }),
  171. )
  172. }
  173. pub(crate) fn debug(args: LogArgs) -> Result<TokenStream> {
  174. log(
  175. args,
  176. Some(quote! { ::aya_log_ebpf::macro_support::Level::Debug }),
  177. )
  178. }
  179. pub(crate) fn trace(args: LogArgs) -> Result<TokenStream> {
  180. log(
  181. args,
  182. Some(quote! { ::aya_log_ebpf::macro_support::Level::Trace }),
  183. )
  184. }