use std::convert::TryFrom;
use std::fs::OpenOptions;
use std::os::unix::io::AsRawFd;
use crate::record::{emit_probe_record, process_section};
use crate::{common, Probe, Provider};
use dof::{serialize_section, Section};
use proc_macro2::TokenStream;
use quote::quote;
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 compile_probe(
provider: &Provider,
probe: &Probe,
config: &crate::CompileProvidersConfig,
) -> TokenStream {
let (unpacked_args, in_regs) = common::construct_probe_args(&probe.types);
let is_enabled_rec = emit_probe_record(&provider.name, &probe.name, None);
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 impl_block = quote! {
{
let mut is_enabled: u64;
unsafe {
::std::arch::asm!(
"990: clr rax",
#is_enabled_rec,
out("rax") is_enabled,
options(nomem, nostack)
);
}
if is_enabled != 0 {
#unpacked_args
#type_check_fn
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)
}
fn extract_probe_records_from_section() -> Result<Section, crate::Error> {
unsafe extern "C" {
#[link_name = "__start_set_dtrace_probes"]
static dtrace_probes_start: usize;
#[link_name = "__stop_set_dtrace_probes"]
static dtrace_probes_stop: usize;
}
#[cfg(any(target_os = "illumos", target_os = "freebsd"))]
#[link_section = "set_dtrace_probes"]
#[used]
static mut FORCE_LOAD: [u64; 0] = [];
let data = unsafe {
let start = (&dtrace_probes_start as *const usize) as usize;
let stop = (&dtrace_probes_stop as *const usize) as usize;
std::slice::from_raw_parts_mut(start as *mut u8, stop - start)
};
process_section(data, true)
}
pub fn register_probes() -> Result<(), crate::Error> {
let section = extract_probe_records_from_section()?;
let module_name = section
.providers
.values()
.next()
.and_then(|provider| {
provider.probes.values().next().and_then(|probe| {
crate::record::addr_to_info(probe.address)
.1
.map(|path| path.rsplit('/').next().map(String::from).unwrap_or(path))
.or_else(|| Some(format!("?{:#x}", probe.address)))
})
})
.unwrap_or_else(|| String::from("unknown-module"));
let mut modname = [0; 64];
for (i, byte) in module_name.bytes().take(modname.len() - 1).enumerate() {
modname[i] = byte as i8;
}
ioctl_section(&serialize_section(§ion), modname)
}
fn ioctl_section(buf: &[u8], modname: [std::os::raw::c_char; 64]) -> Result<(), crate::Error> {
let helper = dof::dof_bindings::dof_helper {
dofhp_mod: modname,
dofhp_addr: buf.as_ptr() as u64,
dofhp_dof: buf.as_ptr() as u64,
#[cfg(target_os = "freebsd")]
dofhp_pid: std::process::id() as i32,
#[cfg(target_os = "freebsd")]
dofhp_gen: 0,
};
let data = &helper as *const _;
#[cfg(target_os = "illumos")]
let cmd: i32 = 0x64746803;
#[cfg(target_os = "freebsd")]
let cmd: u64 = 0xc0587a03;
let file = OpenOptions::new()
.read(true)
.write(true)
.open("/dev/dtrace/helper")?;
if unsafe { libc::ioctl(file.as_raw_fd(), cmd, data) } < 0 {
Err(crate::Error::IO(std::io::Error::last_os_error()))
} else {
Ok(())
}
}