#![no_std]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::alloc::Layout;
use core::{fmt, slice};
#[allow(non_snake_case)]
#[repr(C)]
struct ProfDataIOVec {
Data: *mut u8,
ElmSize: usize,
NumElm: usize,
UseZeroPadding: i32,
}
#[allow(non_snake_case)]
#[repr(C)]
struct ProfDataWriter {
Write:
unsafe extern "C" fn(This: *mut ProfDataWriter, *mut ProfDataIOVec, NumIOVecs: u32) -> u32,
WriterCtx: *mut u8,
}
enum VPDataReaderType {}
extern "C" {
fn __llvm_profile_begin_counters() -> *const u8;
fn __llvm_profile_end_counters() -> *const u8;
fn __llvm_profile_reset_counters();
fn __llvm_profile_merge_from_buffer(profile: *const u8, size: u64) -> i32;
fn __llvm_profile_check_compatibility(profile: *const u8, size: u64) -> i32;
fn __llvm_profile_get_version() -> u64;
fn lprofWriteData(
Writer: *mut ProfDataWriter,
VPDataReader: *mut VPDataReaderType,
SkipNameDataWrite: i32,
) -> i32;
fn lprofGetVPDataReader() -> *mut VPDataReaderType;
fn lprofGetLoadModuleSignature() -> u64;
}
const INSTR_PROF_RAW_VERSION: u64 = 10;
const VARIANT_MASKS_ALL: u64 = 0xffffffff00000000;
#[no_mangle]
static __llvm_profile_runtime: u8 = 0;
#[cfg(feature = "alloc")]
#[no_mangle]
unsafe fn minicov_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
alloc::alloc::alloc_zeroed(Layout::from_size_align(size, align).unwrap())
}
#[cfg(feature = "alloc")]
#[no_mangle]
unsafe extern "C" fn minicov_dealloc(ptr: *mut u8, size: usize, align: usize) {
alloc::alloc::dealloc(ptr, Layout::from_size_align(size, align).unwrap())
}
#[cfg(not(feature = "alloc"))]
#[no_mangle]
unsafe fn minicov_alloc_zeroed(_size: usize, _align: usize) -> *mut u8 {
core::ptr::null_mut()
}
#[cfg(not(feature = "alloc"))]
#[no_mangle]
unsafe extern "C" fn minicov_dealloc(_ptr: *mut u8, _size: usize, _align: usize) {}
pub trait CoverageWriter {
fn write(&mut self, data: &[u8]) -> Result<(), CoverageWriteError>;
}
#[cfg(feature = "alloc")]
impl CoverageWriter for Vec<u8> {
fn write(&mut self, data: &[u8]) -> Result<(), CoverageWriteError> {
self.extend_from_slice(data);
Ok(())
}
}
unsafe extern "C" fn write_callback<Writer: CoverageWriter>(
this: *mut ProfDataWriter,
iovecs: *mut ProfDataIOVec,
num_iovecs: u32,
) -> u32 {
let writer = &mut *((*this).WriterCtx as *mut Writer);
for iov in slice::from_raw_parts(iovecs, num_iovecs as usize) {
let len = iov.ElmSize * iov.NumElm;
if iov.Data.is_null() {
let zero = [0; 16];
let mut remaining = len;
while remaining != 0 {
let data = &zero[..usize::min(zero.len(), remaining)];
if writer.write(data).is_err() {
return 1;
}
remaining -= data.len();
}
} else {
let data = slice::from_raw_parts(iov.Data, len);
if writer.write(data).is_err() {
return 1;
}
}
}
0
}
fn check_version() {
let version = unsafe { __llvm_profile_get_version() & !VARIANT_MASKS_ALL };
assert_eq!(
version, INSTR_PROF_RAW_VERSION,
"Runtime and instrumentation version mismatch"
);
}
pub fn coverage_enabled() -> bool {
unsafe { __llvm_profile_begin_counters() != __llvm_profile_end_counters() }
}
pub unsafe fn capture_coverage<Writer: CoverageWriter>(
writer: &mut Writer,
) -> Result<(), CoverageWriteError> {
check_version();
let mut prof_writer = ProfDataWriter {
Write: write_callback::<Writer>,
WriterCtx: writer as *mut Writer as *mut u8,
};
let res = lprofWriteData(&mut prof_writer, lprofGetVPDataReader(), 0);
if res == 0 {
Ok(())
} else {
Err(CoverageWriteError)
}
}
#[derive(Copy, Clone, Debug)]
pub struct IncompatibleCoverageData;
impl fmt::Display for IncompatibleCoverageData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("incompatible coverage data")
}
}
#[derive(Copy, Clone, Debug)]
pub struct CoverageWriteError;
impl fmt::Display for CoverageWriteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("error while writing coverage data")
}
}
pub unsafe fn merge_coverage(data: &[u8]) -> Result<(), IncompatibleCoverageData> {
check_version();
if __llvm_profile_check_compatibility(data.as_ptr(), data.len() as u64) == 0
&& __llvm_profile_merge_from_buffer(data.as_ptr(), data.len() as u64) == 0
{
Ok(())
} else {
Err(IncompatibleCoverageData)
}
}
pub fn reset_coverage() {
check_version();
unsafe {
__llvm_profile_reset_counters();
}
}
pub fn module_signature() -> u64 {
unsafe { lprofGetLoadModuleSignature() }
}