use crate::build_rs::BuildInfo;
use crate::spec::ProbeArgSpecification;
use crate::spec::ProbeSpecification;
use crate::spec::ProviderInitSpecification;
use crate::spec::ProviderSpecification;
use crate::TracersResult;
use heck::SnakeCase;
use proc_macro2::TokenStream;
use quote::ToTokens;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
pub(super) trait ProviderTraitGeneratorBase {
fn spec(&self) -> &ProviderSpecification;
fn build_info(&self) -> &BuildInfo;
fn generate_trait_comment(&self) -> TokenStream {
let comment = format!(
r###"
# Probing
This trait is translated at compile-time by `tracers` into a platform-specific tracing
provider, which allows very high-performance and low-overhead tracing of the probes it
fires.
The exact details of how to use interact with the probes depends on the underlying
probing implementation.
## SystemTap/USDT (Linux x64)
This trait corresponds to a SystemTap/USDT provider named `{provider_name}`,
## Other platforms
TODO: No other platforms supported yet
"###,
provider_name = self.spec().name()
);
generate_multiline_comments(&comment)
}
fn generate_try_init_decl(&self) -> TokenStream {
let vis = &self.spec().item_trait().vis;
quote! {
#[allow(dead_code)]
#vis fn __try_init_provider() -> ::core::result::Result<&'static str, &'static str>
}
}
fn get_provider_impl_mod_name(&self) -> syn::Ident {
let snake_case_name = get_provider_impl_mod_name(&self.spec().item_trait().ident);
syn::Ident::new(&snake_case_name, self.spec().item_trait().ident.span())
}
fn get_provider_impl_struct_type_name(&self) -> syn::Ident {
crate::syn_helpers::add_suffix_to_ident(&self.spec().item_trait().ident, "ProviderImpl")
}
}
pub(super) trait ProbeGeneratorBase {
fn spec(&self) -> &ProbeSpecification;
fn generate_probe_deprecation_attribute(
&self,
provider: &ProviderSpecification,
) -> TokenStream {
let deprecation_message = format!( "Probe methods should not be called directly. Use the `probe!` macro, e.g. `probe!({}::{}(...))`",
provider.item_trait().ident,
self.spec().method_name);
let span = self.spec().method_name.span();
quote_spanned! {span=>
#[deprecated(note = #deprecation_message)]
}
}
fn generate_probe_doc_comment(&self, provider: &ProviderSpecification) -> TokenStream {
let probe_comment = format!(r###"
# Probing
This method is translated at compile-time by `tracers` into a platform-specific tracing
probe, which allows very high-performance and low-overhead tracing.
## How to fire probe
To fire this probe, don't call this method directly. Instead, use the `probe!` macro, for example:
```ignore
// If the probe is enabled, fires the probe. If the probe isn't enabled, or if provider
// initialization failed for some reason, does not fire the probe, and does NOT evaluate the
// arguments to the probe.
probe!({trait_name}::{probe_name}(...));
```
The exact details of how to interact with the probes depends on the underlying
probing implementation.
## SystemTap/USDT (Linux x64)
To trace the firing of this probe, use `bpftrace`, e.g.:
```text
sudo bpftrace -p ${{PID}} -e 'usdt::{provider}:{probe_name} {{ printf("Hello from {probe_name}\n"); }}'
```
where `${{PID}}` should be the actual process ID of the process you are tracing.
## Other platforms
TODO: No other platforms supported yet
"###,
trait_name = &provider.item_trait().ident,
probe_name = &self.spec().name,
provider = provider.name(),
);
generate_multiline_comments(&probe_comment)
}
fn args_lifetime_parameters(&self) -> Vec<syn::Lifetime> {
self.spec()
.args
.iter()
.map(ProbeArgSpecification::lifetimes)
.flatten()
.collect::<Vec<syn::Lifetime>>()
}
fn args_as_tuple_value(&self) -> TokenStream {
let names = self.spec().args.iter().map(ProbeArgSpecification::ident);
generate_tuple(names)
}
fn args_as_tuple_type_without_lifetimes(&self) -> TokenStream {
let args = self.spec().args.iter().map(ProbeArgSpecification::syn_typ);
generate_tuple(args)
}
fn args_as_tuple_type_with_lifetimes(&self) -> TokenStream {
let types = self
.spec()
.args
.iter()
.map(ProbeArgSpecification::syn_typ_with_lifetimes);
generate_tuple(types)
}
}
pub(super) fn get_provider_impl_mod_name(trait_ident: &syn::Ident) -> String {
format!("__{}", format!("{}Provider", trait_ident).to_snake_case())
}
pub(super) fn generate_tuple<T: ToTokens, I: IntoIterator<Item = T>>(elements: I) -> TokenStream {
let mut elements = elements.into_iter().peekable();
if elements.peek().is_none() {
quote! { () }
} else {
quote! { ( #(#elements),*, )}
}
}
pub(super) fn generate_init_provider(
init: ProviderInitSpecification,
) -> TracersResult<TokenStream> {
let provider = init.provider;
let span = provider.span();
Ok(quote_spanned! {span=>
#provider::__try_init_provider()
})
}
fn generate_multiline_comments(comment: &str) -> TokenStream {
let lines = comment.lines().map(|line| {
let with_leading_space = format!(" {}", line);
quote! {
#[doc = #with_leading_space]
}
});
quote! {
#(#lines)*
}
}