calltrace-rs 1.1.4

High-performance function call tracing library for C/C++ applications using GCC instrumentation with Rust safety guarantees
Documentation
//! GCC Instrumentation Profile Hooks
//!
//! Implements __cyg_profile_func_enter and __cyg_profile_func_exit hooks
//! that are called by GCC when functions are instrumented with -finstrument-functions

use crate::error::{CallTraceError, Result};
use std::cell::RefCell;
use std::ffi::c_void;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};

// Global error tracking
static ERROR_COUNT: AtomicU64 = AtomicU64::new(0);
static TRACING_ENABLED: AtomicBool = AtomicBool::new(true);

// Thread-local recursion guard
thread_local! {
    static IN_HOOK: RefCell<bool> = const { RefCell::new(false) };
}

/// Function entry hook called by GCC instrumentation
#[no_mangle]
pub extern "C" fn __cyg_profile_func_enter(func_address: *mut c_void, call_site: *mut c_void) {
    // Fast path: check if tracing is disabled
    if !TRACING_ENABLED.load(Ordering::Relaxed) {
        return;
    }

    // Prevent recursion from within our own code
    let _guard = match RecursionGuard::new() {
        Some(guard) => guard,
        None => return, // Already in hook, skip
    };

    // Handle errors with graceful degradation
    if let Err(e) = handle_function_enter(func_address, call_site) {
        handle_hook_error(e);
    }
}

/// Function exit hook called by GCC instrumentation  
#[no_mangle]
pub extern "C" fn __cyg_profile_func_exit(func_address: *mut c_void, call_site: *mut c_void) {
    // Fast path: check if tracing is disabled
    if !TRACING_ENABLED.load(Ordering::Relaxed) {
        return;
    }

    // Prevent recursion from within our own code
    let _guard = match RecursionGuard::new() {
        Some(guard) => guard,
        None => return, // Already in hook, skip
    };

    // Handle errors with graceful degradation
    if let Err(e) = handle_function_exit(func_address, call_site) {
        handle_hook_error(e);
    }
}

/// Internal function entry handler
fn handle_function_enter(func_address: *mut c_void, call_site: *mut c_void) -> Result<()> {
    // Call the main library logic
    crate::handle_function_enter_internal(func_address, call_site)
        .map_err(|e| CallTraceError::RegisterError(format!("{:?}", e)))
}

/// Internal function exit handler
fn handle_function_exit(func_address: *mut c_void, call_site: *mut c_void) -> Result<()> {
    // Call the main library logic
    crate::handle_function_exit_internal(func_address, call_site)
        .map_err(|e| CallTraceError::RegisterError(format!("{:?}", e)))
}

/// Handle errors in hooks with graceful degradation
fn handle_hook_error(error: CallTraceError) {
    // Increment error counter
    let error_count = ERROR_COUNT.fetch_add(1, Ordering::Relaxed) + 1;

    // After too many errors, disable tracing to prevent performance impact
    const MAX_ERRORS: u64 = 1000;
    if error_count >= MAX_ERRORS {
        TRACING_ENABLED.store(false, Ordering::Relaxed);
        // In a real implementation, we might log this event to stderr
        // eprintln!("CallTrace: Disabled after {} errors", error_count);
    }

    // For critical errors, disable immediately
    match error {
        CallTraceError::OutOfMemory => {
            TRACING_ENABLED.store(false, Ordering::Relaxed);
        }
        _ => {
            // Other errors: continue with degraded functionality
        }
    }
}

/// RAII guard to prevent recursion in hooks
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 // Already in hook
            } else {
                *in_hook = true;
                Some(RecursionGuard)
            }
        })
    }
}

impl Drop for RecursionGuard {
    fn drop(&mut self) {
        IN_HOOK.with(|in_hook| {
            *in_hook.borrow_mut() = false;
        });
    }
}

/// Get current error count (for debugging)
pub fn get_error_count() -> u64 {
    ERROR_COUNT.load(Ordering::Relaxed)
}

/// Enable or disable tracing
pub fn set_tracing_enabled(enabled: bool) {
    TRACING_ENABLED.store(enabled, Ordering::Relaxed);
}