tracers-codegen 0.1.0

Contains the compile-time code generation logic which powers the `probe` and `tracers` macros. Do not use this crate directly; see "tracers" for more information.
Documentation
//! Generates C code which implements each probe in terms of the target platform's tracing system,
//! if there is one.
use crate::cache;
use crate::probe_spec::ProbeSpecification;
use crate::provider_discovery::ProviderSpecification;
use askama::Template;
use failure::Fallible;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::path::PathBuf;

/// Given a tracing provider, generates the (C/C++) code to wrap the probes in a Rust-callable C
/// form.
///
/// On success, returns the path to the generated source file.  This will be somewhere in the
/// `cache_path`, and if something already exists for this provider then it should be re-used.
pub(crate) fn generate_provider_funcs(
    cache_path: &Path,
    provider: &ProviderSpecification,
) -> Fallible<PathBuf> {
    let file_name = provider.static_provider_source_filename();

    //Make an internal func so we can use the `?` operator which inexplicably doesn't work in
    //closures
    cache::cache_generated_file(cache_path, &file_name, |abs_path| {
        generate_source_file(provider, abs_path)
    })
}

//The awsome Askama templating crate works by declaring structs that will contain the info we need
#[derive(Template)]
#[template(path = "provider_wrapper.c", escape = "none")]
struct SourceFile<'a> {
    package_name: &'a str,
    package_version: &'a str,
    trait_name: String,
    trait_token_stream: String,
    provider_name: &'a str,
    provider_name_with_hash: String,
    probes: Vec<Probe<'a>>,
}

impl<'a> SourceFile<'a> {
    fn from_provider_spec<'b: 'a>(provider: &'b ProviderSpecification) -> SourceFile<'a> {
        let probes: Vec<_> = provider
            .probes
            .iter()
            .map(|probe| Probe::from_probe_spec(provider, probe))
            .collect();

        SourceFile {
            package_name: env!("CARGO_PKG_NAME"),
            package_version: env!("CARGO_PKG_VERSION"),
            trait_name: provider.item_trait.ident.to_string(),
            trait_token_stream: provider.token_stream.to_string(),
            provider_name: &provider.name,
            provider_name_with_hash: provider.name_with_hash(),
            probes: probes,
        }
    }
}

struct Probe<'a> {
    name: &'a str,
    args: Vec<ProbeArg>,
}

impl<'a> Probe<'a> {
    fn from_probe_spec<'b: 'a>(
        provider: &'b ProviderSpecification,
        probe: &'b ProbeSpecification,
    ) -> Probe<'a> {
        let args: Vec<_> = probe
            .args
            .iter()
            .map(|(name, typ)| ProbeArg::new(name.ident.to_string(), typ.to_string()))
            .collect();

        Probe {
            name: &probe.name,
            args: args,
        }
    }
}

struct ProbeArg {
    name: String,
    c_type: String,
}

impl ProbeArg {
    fn new(name: String, typ: String) -> ProbeArg {
        ProbeArg { name, c_type: typ }
    }
}

fn generate_source_file(provider: &ProviderSpecification, abs_path: PathBuf) -> Fallible<PathBuf> {
    let mut file = File::create(&abs_path)?;
    let mut writer = BufWriter::new(&mut file);

    //Build the structures which will serve as input to the template engine

    write!(
        writer,
        r###"
/** This file automatically generated by {package_name} {package_version}.  Do not edit
 * this file.
 *
 * This file contains static wrappers for the probes defined in trait {trait_name}.
 *
 * The source code for that trait is:
 *
 * ```rust
 * {trait_tokenstream}
 * ```
 */
"###,
        package_name = env!("CARGO_PKG_NAME"),
        package_version = env!("CARGO_PKG_VERSION"),
        trait_name = provider.item_trait.ident,
        trait_tokenstream = provider.token_stream
    )?;

    write!(
        writer,
        r###"
#include <sys/sdt.h>

extern "C" {{
"###
    )?;

    //for probe in provider.probes.iter() {
    //    generate_probe_wrapper(provider, probe, &writer)?
    //}

    write!(writer, "}}\n")?;

    Ok(abs_path)
}