#[path = "stapsdt/args.rs"]
mod args;
use crate::{common, DataType};
use crate::{Probe, Provider};
use args::format_argument;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::convert::TryFrom;
pub fn compile_provider_source(
source: &str,
config: &crate::CompileProvidersConfig,
) -> Result<TokenStream, crate::Error> {
let dfile = dtrace_parser::File::try_from(source)?;
let providers = dfile
.providers()
.iter()
.map(|provider| {
let provider = Provider::from(provider);
let config = crate::CompileProvidersConfig {
provider: Some(provider.name.clone()),
probe_format: config.probe_format.clone(),
module: match &config.module {
None => Some(provider.name.clone()),
other => other.clone(),
},
};
compile_provider(&provider, &config)
})
.collect::<Vec<_>>();
Ok(quote! {
#(#providers)*
})
}
pub fn compile_provider_from_definition(
provider: &Provider,
config: &crate::CompileProvidersConfig,
) -> TokenStream {
compile_provider(provider, config)
}
fn compile_provider(provider: &Provider, config: &crate::CompileProvidersConfig) -> TokenStream {
let probe_impls = provider
.probes
.iter()
.map(|probe| compile_probe(provider, probe, config))
.collect::<Vec<_>>();
let module = config.module_ident();
quote! {
pub(crate) mod #module {
#(#probe_impls)*
}
}
}
fn emit_probe_record(prov: &str, probe: &str, types: Option<&[DataType]>) -> String {
let sema_name = format!("__usdt_sema_{}_{}", prov, probe);
let arguments = types.map_or_else(String::new, |types| {
types
.iter()
.enumerate()
.map(format_argument)
.collect::<Vec<_>>()
.join(" ")
});
format!(
r#"// First define the semaphore
// Note: This uses ifndef to make sure the same probe name can be used
// in multiple places but they all use the same semaphore. This can be
// used to eg. guard additional preparatory work far away from the
// actual probe site that will only be used by the probe.
.ifndef {sema_name}
.pushsection .probes, "aw", "progbits"
.weak {sema_name}
.hidden {sema_name}
{sema_name}:
.zero 2
.type {sema_name}, @object
.size {sema_name}, 2
.popsection
.endif
// Second define the actual USDT probe
.pushsection .note.stapsdt, "", "note"
.balign 4
.4byte 992f-991f, 994f-993f, 3 // length, type
991:
.asciz "stapsdt" // vendor string
992:
.balign 4
993:
.8byte 990b // probe PC address
.8byte _.stapsdt.base // link-time sh_addr of base .stapsdt.base section
.8byte {sema_name} // probe semaphore address
.asciz "{prov}" // provider name
.asciz "{probe}" // probe name
.asciz "{arguments}" // argument format (null-terminated string)
994:
.balign 4
.popsection
// Finally define (if not defined yet) the base used to detect prelink
// address adjustments.
.ifndef _.stapsdt.base
.pushsection .stapsdt.base, "aGR", "progbits", .stapsdt.base, comdat
.weak _.stapsdt.base
.hidden _.stapsdt.base
_.stapsdt.base:
.space 1
.size _.stapsdt.base, 1
.popsection
.endif"#,
prov = prov,
probe = probe.replace("__", "-"),
arguments = arguments,
)
}
fn compile_probe(
provider: &Provider,
probe: &Probe,
config: &crate::CompileProvidersConfig,
) -> TokenStream {
let (unpacked_args, in_regs) = common::construct_probe_args(&probe.types);
let probe_rec = emit_probe_record(&provider.name, &probe.name, Some(&probe.types));
let type_check_fn = common::construct_type_check(
&provider.name,
&probe.name,
&provider.use_statements,
&probe.types,
);
let sema_name = format_ident!("__usdt_sema_{}_{}", provider.name, probe.name);
let impl_block = quote! {
unsafe extern "C" {
static #sema_name: u16;
}
let is_enabled: u16;
unsafe {
is_enabled = (&raw const #sema_name).read_volatile();
}
if is_enabled != 0 {
#unpacked_args
#type_check_fn
#[allow(named_asm_labels)]
unsafe {
::std::arch::asm!(
"990: nop",
#probe_rec,
#in_regs
options(nomem, nostack, preserves_flags)
);
}
}
};
common::build_probe_macro(config, &probe.name, &probe.types, impl_block)
}
pub fn register_probes() -> Result<(), crate::Error> {
Ok(())
}