use crate::argtypes::{
wrap, CType, ProbeArgNativeType, ProbeArgNativeTypeInfo, ProbeArgType, ProbeArgWrapper,
};
use failure::Fallible;
use std::marker::PhantomData;
pub trait Tracer: Sized {
const TRACING_IMPLEMENTATION: &'static str;
type ProviderBuilderType: ProviderBuilder<Self>;
type ProviderType: Provider<Self> + Sync + Send;
type ProbeType: UnsafeProviderProbeImpl + Sync + Send;
fn define_provider(
name: &str,
f: impl FnOnce(Self::ProviderBuilderType) -> Fallible<Self::ProviderBuilderType>,
) -> Fallible<Self::ProviderType>;
}
pub trait ProviderBuilder<TracerT: Tracer> {
fn add_probe<ArgsT: ProbeArgs<ArgsT>>(&mut self, name: &'static str) -> Fallible<()>;
fn build(self, name: &str) -> Fallible<<TracerT as Tracer>::ProviderType>;
}
pub trait Provider<TracerT: Tracer> {
fn get_probe<ArgsT: ProbeArgs<ArgsT>>(
&self,
name: &'static str,
) -> Fallible<ProviderProbe<<TracerT as Tracer>::ProbeType, ArgsT>> {
let definition = ProbeDefinition::new::<ArgsT>(name);
let unsafe_impl = self.get_probe_unsafe(&definition)?;
Ok(ProviderProbe::new(unsafe_impl))
}
fn get_probe_unsafe(
&self,
definition: &ProbeDefinition,
) -> Fallible<&<TracerT as Tracer>::ProbeType>;
}
#[derive(Copy, Clone)]
pub struct ProviderProbe<'probe, ImplT: UnsafeProviderProbeImpl, ArgsT: ProbeArgs<ArgsT>> {
unsafe_probe_impl: &'probe ImplT,
_args: PhantomData<ArgsT>,
}
impl<'probe, ImplT: UnsafeProviderProbeImpl, ArgsT: ProbeArgs<ArgsT>>
ProviderProbe<'probe, ImplT, ArgsT>
{
fn new(probe: &'probe ImplT) -> Self {
ProviderProbe {
unsafe_probe_impl: probe,
_args: PhantomData,
}
}
pub fn is_enabled(&self) -> bool {
self.unsafe_probe_impl.is_enabled()
}
pub fn fire(&self, args: ArgsT) {
args.fire_probe(self.unsafe_probe_impl)
}
}
unsafe impl<'probe, ImplT: UnsafeProviderProbeImpl + Sync, ArgsT: ProbeArgs<ArgsT>> Sync
for ProviderProbe<'probe, ImplT, ArgsT>
{
}
unsafe impl<'probe, ImplT: UnsafeProviderProbeImpl + Send, ArgsT: ProbeArgs<ArgsT>> Send
for ProviderProbe<'probe, ImplT, ArgsT>
{
}
pub trait ProbeArgs<T> {
const ARG_COUNT: usize;
fn arg_types() -> Vec<CType>;
fn fire_probe<ImplT: UnsafeProviderProbeImpl>(self, probe: &ImplT);
}
#[derive(Clone, Debug, PartialEq, Hash, Eq)]
pub struct ProbeDefinition {
pub name: &'static str,
pub arg_types: Vec<CType>,
}
impl ProbeDefinition {
pub fn new<ArgsT: ProbeArgs<ArgsT>>(name: &'static str) -> ProbeDefinition {
ProbeDefinition {
name,
arg_types: <ArgsT as ProbeArgs<ArgsT>>::arg_types(),
}
}
}
fn get_ctype<T: ProbeArgType<T>>() -> CType {
<<<T as ProbeArgType<T>>::WrapperType as ProbeArgWrapper>::CType as ProbeArgNativeTypeInfo>::get_c_type()
}
include!(concat!(env!("OUT_DIR"), "/probe_args.rs"));
#[cfg(test)]
#[cfg(unix)] mod test {
use super::*;
use crate::argtypes::ProbeArgNativeType;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::sync::Mutex;
const BUFFER_SIZE: usize = 8192;
struct TestingProviderProbeImpl {
pub is_enabled: bool,
pub format_string: CString,
pub buffer: Mutex<Vec<c_char>>,
pub calls: Mutex<Vec<String>>,
}
impl TestingProviderProbeImpl {
pub fn new(format_string: String) -> TestingProviderProbeImpl {
let c_format_string = CString::new(format_string).expect("invalid string");
TestingProviderProbeImpl {
is_enabled: false,
format_string: c_format_string,
buffer: Mutex::new(Vec::with_capacity(BUFFER_SIZE)),
calls: Mutex::new(Vec::new()),
}
}
fn log_call(&self) {
let buffer = self.buffer.lock().unwrap();
let mut calls = self.calls.lock().unwrap();
let cstring = unsafe { CStr::from_ptr(buffer.as_ptr()) };
let as_str = cstring.to_str().expect("snprintf string isn't valid UTF-8");
calls.push(as_str.to_string());
}
fn get_calls(&self) -> Vec<String> {
self.calls.lock().unwrap().clone()
}
}
fn c_and_back_again(arg: &str) -> String {
CString::new(arg)
.map(|s| s.to_str().expect("Invalid UTF-8").to_string())
.unwrap_or("(null)".to_string()) }
include!(concat!(env!("OUT_DIR"), "/probe_args_tests.rs"));
#[test]
fn test_fire0() {
let unsafe_impl = TestingProviderProbeImpl::new("hey the probe fired".to_string());
let probe_impl = ProviderProbe::new(&unsafe_impl);
probe_impl.fire(());
assert_eq!(
probe_impl.unsafe_probe_impl.get_calls(),
vec!["hey the probe fired"]
);
}
}