# Teleprof A lightweight, debug-only telemetry profiler for Rust applications. Shows thread activity and call stack hierarchy in real-time. Inspired by RAD Telemetry - built in ~400 LOC with minimal dependencies. ## Features - **Icicle graph** showing call stack hierarchy (top half) - **Thread timeline** showing per-thread activity over time (bottom half) - **Monokai color palette** for easy visual distinction - **Pause mechanism** to freeze your application for inspection - **Ringbuffer storage** (~16MB, 1M events) for recent history - **Lock-free event recording** via MPSC channels ## Dependencies Only 3 dependencies (~15 total including transitive): - `minifb` - Window and framebuffer - `crossbeam-channel` - Lock-free MPSC - `once_cell` - Lazy statics ## Usage ### Add to your `Cargo.toml`: ```toml [dependencies] teleprof = { path = "../teleprof" } # or from crates.io when published ``` ### In your code: ```rust fn main() { // Start the profiler window (separate thread) #[cfg(debug_assertions)] teleprof::start(); // Your application code game_loop(); } fn game_loop() { loop { // Profile a scope teleprof::span!("game_loop"); update(); render(); // Check if paused (optional) if teleprof::PAUSE.try_lock().is_err() { // Wait until unpaused while teleprof::PAUSE.try_lock().is_err() { std::thread::sleep(std::time::Duration::from_millis(100)); } } } } fn update() { teleprof::span!("update"); // Your update code } fn render() { teleprof::span!("render"); // Your render code } ``` ### For closures: ```rust let work = || { teleprof::span!("my_closure"); // work... }; ``` ## Controls - **Space**: Toggle pause (acquires `PAUSE` lock to freeze your app) - **Escape**: Close profiler window ## How it works 1. `span!()` macro creates a `SpanGuard` that sends `SpanStart` on creation 2. When the guard drops, sends `SpanEnd` 3. Events are sent via lock-free MPSC channel 4. Window thread drains events into a fixed-size ringbuffer 5. Renders icicle graph (call hierarchy) and timeline (per-thread activity) ## Design Goals - **Minimal overhead**: Lock-free event recording - **Debug-only**: Compile out in release builds with `#[cfg(debug_assertions)]` - **Separate window**: Doesn't interfere with your app's rendering - **Simple API**: Just `span!("name")` and you're done ## Example Output ``` ┌─────────────────────────────────────┐ │ Icicle Graph (Call Stack) │ │ ┌──────────────────────────┐ │ │ │ frame_work │ │ │ ├──────────┬───────────────┤ │ │ │ physics │ render │ │ │ ├────┬─────┤ │ │ │ │ w0 │ w1 │ │ │ │ └────┴─────┴───────────────┘ │ ├─────────────────────────────────────┤ │ Thread Timeline │ │ Main: ████████████████████ │ │ Work 0: ░░██████░░░░░░░░░░░ │ │ Work 1: ░░░░░░██████░░░░░░░ │ └─────────────────────────────────────┘ ``` ## Examples Run the included examples: ```bash # Multi-threaded physics simulation cargo run --example demo # Bouncing ball with color-picking thread (30 FPS) cargo run --example bouncing_ball ``` The bouncing ball example demonstrates: - Main thread running at 30 FPS with clear frame gaps - Background thread spawned on wall collision to pick colors - Clear visual separation between thread activities ## License MIT / Apache-2.0 (choose whichever you prefer)