rsprof_trace/
lib.rs

1//! Self-instrumentation library for rsprof.
2//!
3//! This crate provides CPU and heap profiling through self-instrumentation:
4//! - **CPU profiling**: Timer-based sampling using SIGPROF
5//! - **Heap profiling**: Custom allocator that tracks allocations
6//!
7//! # Usage
8//!
9//! Add to your `Cargo.toml`:
10//! ```toml
11//! [dependencies]
12//! rsprof-trace = { version = "0.1", features = ["profiling"] }
13//! ```
14//!
15//! For CPU profiling only:
16//! ```rust,ignore
17//! fn main() {
18//!     // Start CPU profiling at 99Hz
19//!     rsprof_trace::start_cpu_profiling(99);
20//!
21//!     // Your application code...
22//!
23//!     // Stop profiling (optional, stops on process exit)
24//!     rsprof_trace::stop_cpu_profiling();
25//! }
26//! ```
27//!
28//! For heap profiling, use the global allocator:
29//! ```rust,ignore
30//! #[global_allocator]
31//! static ALLOC: rsprof_trace::ProfilingAllocator = rsprof_trace::ProfilingAllocator;
32//! ```
33//!
34//! Build with frame pointers for accurate stack traces:
35//! ```bash
36//! RUSTFLAGS="-C force-frame-pointers=yes" cargo build --release --features profiling
37//! ```
38
39#![no_std]
40
41extern crate alloc;
42
43// Include profiling module when any profiling feature is enabled
44#[cfg(any(feature = "heap", feature = "cpu"))]
45mod profiling;
46
47// Re-export CPU profiling functions
48#[cfg(feature = "cpu")]
49pub use profiling::{start_cpu_profiling, stop_cpu_profiling};
50
51// Stubs when CPU feature is disabled
52#[cfg(not(feature = "cpu"))]
53#[inline]
54pub fn start_cpu_profiling(_freq_hz: u32) {}
55
56#[cfg(not(feature = "cpu"))]
57#[inline]
58pub fn stop_cpu_profiling() {}
59
60/// A profiling allocator that wraps the system allocator.
61///
62/// When the `heap` feature is enabled, this allocator captures
63/// allocation and deallocation events along with stack traces.
64/// When disabled, it's a zero-cost passthrough to the system allocator.
65pub struct ProfilingAllocator;
66
67#[cfg(not(feature = "heap"))]
68mod disabled {
69    use super::ProfilingAllocator;
70    use core::alloc::{GlobalAlloc, Layout};
71
72    unsafe impl GlobalAlloc for ProfilingAllocator {
73        #[inline]
74        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
75            unsafe { libc::malloc(layout.size()) as *mut u8 }
76        }
77
78        #[inline]
79        unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
80            unsafe { libc::free(ptr as *mut libc::c_void) }
81        }
82
83        #[inline]
84        unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
85            unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 }
86        }
87
88        #[inline]
89        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
90            unsafe { libc::calloc(1, layout.size()) as *mut u8 }
91        }
92    }
93}
94
95#[cfg(feature = "heap")]
96mod enabled {
97    use super::{ProfilingAllocator, profiling};
98    use core::alloc::{GlobalAlloc, Layout};
99
100    unsafe impl GlobalAlloc for ProfilingAllocator {
101        #[inline]
102        unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
103            let ptr = unsafe { libc::malloc(layout.size()) as *mut u8 };
104            if !ptr.is_null() {
105                profiling::record_alloc(ptr, layout.size());
106            }
107            ptr
108        }
109
110        #[inline]
111        unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
112            profiling::record_dealloc(ptr, layout.size());
113            unsafe { libc::free(ptr as *mut libc::c_void) }
114        }
115
116        #[inline]
117        unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
118            profiling::record_dealloc(ptr, layout.size());
119            let new_ptr = unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 };
120            if !new_ptr.is_null() {
121                profiling::record_alloc(new_ptr, new_size);
122            }
123            new_ptr
124        }
125
126        #[inline]
127        unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
128            let ptr = unsafe { libc::calloc(1, layout.size()) as *mut u8 };
129            if !ptr.is_null() {
130                profiling::record_alloc(ptr, layout.size());
131            }
132            ptr
133        }
134    }
135}