use std::ffi::{c_void, CStr};
mod bindings;
macro_rules! raw_call {
($f:ident) => { raw_call!($f,) };
($f:ident, $($args:tt)*) => {{
unsafe{ bindings::$f($($args)*) }
}};
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub enum RunMode {
Native,
Valgrind,
ValgrindInValgrind(usize),
}
#[inline]
pub fn run_mode() -> RunMode {
match unsafe { bindings::running_on_valgrind() } {
0 => RunMode::Native,
1 => RunMode::Valgrind,
x => RunMode::ValgrindInValgrind(x),
}
}
#[inline]
pub fn print(msg: impl AsRef<CStr>) -> usize {
raw_call!(vg_print, msg.as_ref().as_ptr())
}
#[inline]
pub fn print_backtrace(msg: impl AsRef<CStr>) -> usize {
raw_call!(vg_print_backtrace, msg.as_ref().as_ptr())
}
#[inline]
pub fn monitor_command(cmd: impl AsRef<CStr>) -> bool {
raw_call!(vg_monitor_command, cmd.as_ref().as_ptr())
}
pub mod valgrind {
use std::os::unix::prelude::RawFd;
use super::*;
#[inline]
pub fn disable_error_reporting() {
raw_call!(vg_disable_error_reporting);
}
#[inline]
pub fn enable_error_reporting() {
raw_call!(vg_enable_error_reporting);
}
#[inline]
pub fn count_errors() -> usize {
raw_call!(vg_count_errors)
}
#[inline]
pub fn cli_option_change(opt: impl AsRef<CStr>) {
raw_call!(vg_clo_change, opt.as_ref().as_ptr());
}
#[inline]
pub fn discard_translations(addr: *mut c_void, len: usize) {
raw_call!(vg_discard_translations, addr, len);
}
#[inline]
pub fn load_pdb_debuginfo(fd: RawFd, ptr: *mut c_void, total_size: usize, delta: usize) {
raw_call!(vg_load_pdb_debuginfo, fd, ptr, total_size, delta);
}
#[inline]
pub fn map_ip_to_srcloc(addr: *mut c_void, buf64: *mut c_void) -> usize {
raw_call!(vg_map_ip_to_srcloc, addr, buf64)
}
extern "C" fn _closure_adapter<F>(tid: usize, f: *mut c_void)
where
F: FnMut(usize),
{
debug_assert!(!f.is_null(), "closure pointer is null");
debug_assert_eq!(
f as usize & (std::mem::align_of::<F>() - 1),
0,
"unexpected closure pointer"
);
if let Err(err) = std::panic::catch_unwind(|| unsafe { (*f.cast::<F>())(tid) }) {
let panic_info = err
.downcast::<String>()
.map(|v| *v)
.or_else(|e| e.downcast::<&str>().map(|v| v.to_string()))
.unwrap_or_else(|_| "unknown panic source".to_string());
eprintln!("closure code panicked with: {panic_info:?}");
std::process::abort();
}
}
#[inline]
pub fn non_simd_call<F>(f: F)
where
F: FnMut(usize),
{
let boxed = Box::into_raw(Box::new(f));
raw_call!(vg_non_simd_call1, _closure_adapter::<F>, boxed.cast());
let _ = unsafe { Box::from_raw(boxed) };
}
}
pub mod callgrind {
use super::*;
#[inline]
pub fn dump_stats<R: AsRef<CStr>>(reason: impl Into<Option<R>>) {
match reason.into() {
None => raw_call!(cg_dump_stats),
Some(reason) => raw_call!(cg_dump_stats_at, reason.as_ref().as_ptr()),
};
}
#[inline]
pub fn zero_stats() {
raw_call!(cg_zero_stats);
}
#[inline]
pub fn toggle_collect() {
raw_call!(cg_toggle_collect);
}
#[inline]
pub fn start_instrumentation() {
raw_call!(cg_start_instrumentation);
}
#[inline]
pub fn stop_instrumentation() {
raw_call!(cg_stop_instrumentation);
}
}
pub mod memcheck {
use super::*;
pub use bindings::LeakCount;
pub type BlockDescHandle = u32;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Error {
InvalidHandle,
NotAddressable(usize),
NoValgrind,
UnalignedArrays,
}
impl std::error::Error for Error {}
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
impl std::fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::InvalidHandle => f.write_str("Invalid memory block description handle"),
Error::NotAddressable(addr) => {
write!(f, "Memory starting from 0x{addr:X} is not addressable")
}
Error::NoValgrind => f.write_str("Not running under Valgrind"),
Error::UnalignedArrays => {
f.write_str("[previously indicated unaligned arrays; these are now allowed]")
}
}
}
}
pub type Result<T = ()> = std::result::Result<T, Error>;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub enum LeakCheck {
Full,
Quick,
Added,
Changed,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub enum MemMark {
NoAccess,
Undefined,
Defined,
DefinedIfAddressable,
}
#[inline]
pub fn mark_mem(addr: *mut c_void, len: usize, mark: MemMark) {
match mark {
MemMark::NoAccess => raw_call!(mc_make_mem_noaccess, addr, len),
MemMark::Undefined => raw_call!(mc_make_mem_undefined, addr, len),
MemMark::Defined => raw_call!(mc_make_mem_defined, addr, len),
MemMark::DefinedIfAddressable => {
raw_call!(mc_make_mem_defined_if_addressable, addr, len)
}
};
}
#[inline]
pub fn create_block(addr: *mut c_void, len: usize, desc: impl AsRef<CStr>) -> BlockDescHandle {
raw_call!(mc_create_block, addr, len, desc.as_ref().as_ptr())
}
#[inline]
pub fn discard(handle: BlockDescHandle) -> Result {
if raw_call!(mc_discard, handle) == 0 {
Ok(())
} else {
Err(Error::InvalidHandle)
}
}
#[inline]
pub fn is_addressable(addr: *mut c_void, len: usize) -> Result {
match raw_call!(mc_check_mem_is_addressable, addr, len) {
0 => Ok(()),
addr => Err(Error::NotAddressable(addr)),
}
}
#[inline]
pub fn is_defined(addr: *mut c_void, len: usize) -> Result {
match raw_call!(mc_check_mem_is_defined, addr, len) {
0 => Ok(()),
addr => Err(Error::NotAddressable(addr)),
}
}
#[inline]
pub fn leak_check(mode: LeakCheck) {
match mode {
LeakCheck::Full => raw_call!(mc_do_leak_check),
LeakCheck::Quick => raw_call!(mc_do_quick_leak_check),
LeakCheck::Added => raw_call!(mc_do_added_leak_check),
LeakCheck::Changed => raw_call!(mc_do_changed_leak_check),
};
}
#[inline]
pub fn leaks_count() -> LeakCount {
raw_call!(mc_count_leaks)
}
#[inline]
pub fn block_leaks_count() -> LeakCount {
raw_call!(mc_count_leak_blocks)
}
#[inline]
pub fn vbits(addr: *mut c_void, bits: *const u8, nbytes: usize) -> Result {
match raw_call!(mc_get_vbits, addr, bits, nbytes) {
0 => Err(Error::NoValgrind),
1 => Ok(()),
2 => Err(Error::UnalignedArrays),
3 => Err(Error::NotAddressable(0)),
x => unreachable!("Unexpected return code {}", x),
}
}
#[inline]
pub fn set_vbits(addr: *mut c_void, bits: *const u8, nbytes: usize) -> Result {
match raw_call!(mc_set_vbits, addr, bits, nbytes) {
0 => Err(Error::NoValgrind),
1 => Ok(()),
2 => Err(Error::UnalignedArrays),
3 => Err(Error::NotAddressable(0)),
x => unreachable!("Unexpected return code {}", x),
}
}
#[inline]
pub fn disable_error_reporting(addr: *mut c_void, len: usize) {
raw_call!(mc_disable_addr_error_reporting_in_range, addr, len);
}
#[inline]
pub fn enable_error_reporting(addr: *mut c_void, len: usize) {
raw_call!(mc_enable_addr_error_reporting_in_range, addr, len);
}
pub mod alloc {
use super::super::*;
#[inline]
pub fn malloc(addr: *mut c_void, size: usize, rz: usize, is_zeroed: bool) {
raw_call!(vg_malloclike_block, addr, size, rz, is_zeroed);
}
#[inline]
pub fn free(addr: *mut c_void, rz: usize) {
raw_call!(vg_freelike_block, addr, rz);
}
#[inline]
pub fn resize_inplace(addr: *mut c_void, old_size: usize, new_size: usize, rz: usize) {
raw_call!(vg_resizeinplace_block, addr, old_size, new_size, rz);
}
}
pub mod mempool {
use super::super::*;
pub const AUTO_FREE: u32 = 1;
pub const METAPOOL: u32 = 2;
#[inline]
pub fn create(
pool: *mut c_void,
rz: usize,
is_zeroed: bool,
flags: impl Into<Option<u32>>,
) {
match flags.into() {
None => raw_call!(vg_create_mempool, pool, rz, is_zeroed),
Some(flags) => raw_call!(vg_create_mempool_ext, pool, rz, is_zeroed, flags),
};
}
#[inline]
pub fn destroy(pool: *mut c_void) {
raw_call!(vg_destroy_mempool, pool);
}
#[inline]
pub fn alloc(pool: *mut c_void, addr: *mut c_void, size: usize) {
raw_call!(vg_mempool_alloc, pool, addr, size);
}
#[inline]
pub fn free(pool: *mut c_void, addr: *mut c_void) {
raw_call!(vg_mempool_free, pool, addr);
}
#[inline]
pub fn trim(pool: *mut c_void, addr: *mut c_void, size: usize) {
raw_call!(vg_mempool_trim, pool, addr, size);
}
#[inline]
pub fn move_to(pool_a: *mut c_void, pool_b: *mut c_void) {
raw_call!(vg_move_mempool, pool_a, pool_b);
}
#[inline]
pub fn change(pool: *mut c_void, addr_a: *mut c_void, addr_b: *mut c_void, size: usize) {
raw_call!(vg_mempool_change, pool, addr_a, addr_b, size);
}
#[inline]
pub fn is_exists(pool: *mut c_void) -> bool {
raw_call!(vg_mempool_exists, pool)
}
}
pub mod stack {
use super::super::*;
pub type StackId = usize;
#[inline]
pub fn register(start: *mut c_void, end: *mut c_void) -> StackId {
raw_call!(vg_stack_register, start, end)
}
#[inline]
pub fn deregister(id: StackId) {
raw_call!(vg_stack_deregister, id);
}
#[inline]
pub fn change(id: StackId, start: *mut c_void, end: *mut c_void) {
raw_call!(vg_stack_change, id, start, end);
}
}
}
pub mod helgrind {
use super::*;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub enum Annotation {
HappensBefore,
HappensAfter,
New(usize),
RwLockCreate,
RwLockDestroy,
RwLockAcquired(bool),
RwLockReleased,
}
#[inline]
pub fn clean_memory(addr: *mut c_void, len: usize) {
raw_call!(hg_clean_memory, addr, len);
}
#[inline]
pub fn annotate_memory(addr: *mut c_void, rel: Annotation) {
match rel {
Annotation::HappensBefore => raw_call!(hg_annotate_happens_before, addr),
Annotation::HappensAfter => raw_call!(hg_annotate_happens_after, addr),
Annotation::New(size) => raw_call!(hg_annotate_new_memory, addr, size),
Annotation::RwLockCreate => raw_call!(hg_rwlock_create, addr),
Annotation::RwLockDestroy => raw_call!(hg_rwlock_destroy, addr),
Annotation::RwLockAcquired(is_wl) => raw_call!(hg_rwlock_acquired, addr, is_wl),
Annotation::RwLockReleased => raw_call!(hg_rwlock_released, addr),
};
}
}