use failure::{bail, Fallible};
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
const MAX_ARITY: usize = 12; const STAP_MAX_ARITY: usize = 6;
fn is_enabled() -> bool {
env::var("CARGO_FEATURE_ENABLED").is_ok() || is_required()
}
fn is_required() -> bool {
env::var("CARGO_FEATURE_REQUIRED").is_ok()
}
fn main() {
println!("building tracers-dyn-stap if enabled");
if !is_enabled() {
println!("tracers-dyn-stap is not enabled; build skipped");
return;
}
let fail_on_error = is_required();
match try_build() {
Ok(_) => {
println!("cargo:rustc-cfg=enabled");
println!("cargo:succeeded=1"); }
Err(e) => {
if fail_on_error {
panic!("tracers-dyn-stap build failed: {}", e);
} else {
println!("cargo:warning=tracers-dyn-stap build failed: {}", e);
println!(
"cargo:warning=the tracers-dyn-stap bindings will not be included in the crate"
);
}
}
}
}
fn try_build() -> Fallible<()> {
if env::var("DEP_STAPSDT_SUCCEEDED").is_err() {
bail!("tracers-dyn-stap is not available because libstapsdt-sys did not build successfully")
}
let out_dir = env::var("OUT_DIR")?;
let dest_path = Path::new(&out_dir).join("probe_unsafe_impl.rs");
let mut f = File::create(&dest_path)?;
f.write_all(generate_stap_native_impl().as_bytes())?;
Ok(())
}
fn get_type_param_names(args: usize) -> Vec<String> {
(0..args).map(|x| format!("T{}", x)).collect()
}
fn xform_types<F: FnMut(&String) -> String>(type_params: &Vec<String>, mut f: F) -> Vec<String> {
type_params.iter().map(|x| f(x)).collect::<Vec<String>>()
}
fn xform_types_i<F: FnMut(usize, &String) -> String>(
type_params: &Vec<String>,
mut f: F,
) -> Vec<String> {
type_params
.iter()
.enumerate()
.map(|(i, x)| f(i, x))
.collect::<Vec<String>>()
}
fn generate_stap_native_impl() -> String {
let mut decl= r#"
/// Implementation of `UnsafeProviderProbeNativeImpl` for SystemTap.
///
/// NB: While the `tracers` API supports probes with from 0 to 12 arguments, the libstapsdt library (or maybe SystemTap itself)
/// support up to 6. This implementation must provide all arities from 0 to 12, but only the first 6 parameters are used.
impl UnsafeProviderProbeNativeImpl for StapProbe
{
fn is_enabled(&self) -> bool { StapProbe::is_enabled(self) }
unsafe fn c_fire0(&self) {
probeFire(self.probe);
}
"#.to_string();
for arity in 1..=MAX_ARITY {
let stap_arg_count = if arity > STAP_MAX_ARITY {
STAP_MAX_ARITY
} else {
arity
};
let type_params = get_type_param_names(arity);
let mut stap_type_params = type_params.clone();
let ignored_type_params = stap_type_params.split_off(stap_arg_count);
let stap_args = xform_types_i(&stap_type_params, |i, x| format!("arg{}: {}", i, x));
let ignored_type_args =
xform_types_i(&ignored_type_params, |i, x| format!("_arg{}: {}", i, x));
let mut all_args = stap_args.clone();
all_args.extend(ignored_type_args.clone());
let stap_arg_names = xform_types_i(&stap_type_params, |i, _| format!("arg{}", i));
decl += &format!(
r##"
#[allow(clippy::duplicate_underscore_argument)]
unsafe fn c_fire{arg_count}<{type_list}>(&self, {args})
where {where_clause} {{
probeFire(self.probe, {stap_arg_names});
}}
"##,
arg_count = type_params.len(),
type_list = type_params.join(","),
args = all_args.join(","),
where_clause = xform_types(&type_params, |x| format!(
"{t}: ProbeArgNativeType<{t}>",
t = x
))
.join(","),
stap_arg_names = stap_arg_names.join(",")
);
}
decl += "}\n";
decl
}