use crate::error::{CallTraceError, Result};
use std::cell::RefCell;
use std::ffi::c_void;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
static ERROR_COUNT: AtomicU64 = AtomicU64::new(0);
static TRACING_ENABLED: AtomicBool = AtomicBool::new(true);
thread_local! {
static IN_HOOK: RefCell<bool> = const { RefCell::new(false) };
}
#[no_mangle]
pub extern "C" fn __cyg_profile_func_enter(func_address: *mut c_void, call_site: *mut c_void) {
if !TRACING_ENABLED.load(Ordering::Relaxed) {
return;
}
let _guard = match RecursionGuard::new() {
Some(guard) => guard,
None => return, };
if let Err(e) = handle_function_enter(func_address, call_site) {
handle_hook_error(e);
}
}
#[no_mangle]
pub extern "C" fn __cyg_profile_func_exit(func_address: *mut c_void, call_site: *mut c_void) {
if !TRACING_ENABLED.load(Ordering::Relaxed) {
return;
}
let _guard = match RecursionGuard::new() {
Some(guard) => guard,
None => return, };
if let Err(e) = handle_function_exit(func_address, call_site) {
handle_hook_error(e);
}
}
fn handle_function_enter(func_address: *mut c_void, call_site: *mut c_void) -> Result<()> {
crate::handle_function_enter_internal(func_address, call_site)
.map_err(|e| CallTraceError::RegisterError(format!("{:?}", e)))
}
fn handle_function_exit(func_address: *mut c_void, call_site: *mut c_void) -> Result<()> {
crate::handle_function_exit_internal(func_address, call_site)
.map_err(|e| CallTraceError::RegisterError(format!("{:?}", e)))
}
fn handle_hook_error(error: CallTraceError) {
let error_count = ERROR_COUNT.fetch_add(1, Ordering::Relaxed) + 1;
const MAX_ERRORS: u64 = 1000;
if error_count >= MAX_ERRORS {
TRACING_ENABLED.store(false, Ordering::Relaxed);
}
match error {
CallTraceError::OutOfMemory => {
TRACING_ENABLED.store(false, Ordering::Relaxed);
}
_ => {
}
}
}
struct RecursionGuard;
impl RecursionGuard {
fn new() -> Option<Self> {
IN_HOOK.with(|in_hook| {
let mut in_hook = in_hook.borrow_mut();
if *in_hook {
None } else {
*in_hook = true;
Some(RecursionGuard)
}
})
}
}
impl Drop for RecursionGuard {
fn drop(&mut self) {
IN_HOOK.with(|in_hook| {
*in_hook.borrow_mut() = false;
});
}
}
pub fn get_error_count() -> u64 {
ERROR_COUNT.load(Ordering::Relaxed)
}
pub fn set_tracing_enabled(enabled: bool) {
TRACING_ENABLED.store(enabled, Ordering::Relaxed);
}