11d9ebe2b63918aff685d9609a4374124526d4b0
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 framebuffercrossbeam-channel- Lock-free MPSConce_cell- Lazy statics
Usage
Add to your Cargo.toml:
[dependencies]
teleprof = { path = "../teleprof" } # or from crates.io when published
In your code:
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:
let work = || {
teleprof::span!("my_closure");
// work...
};
Controls
- Space: Toggle pause (acquires
PAUSElock to freeze your app) - Escape: Close profiler window
How it works
span!()macro creates aSpanGuardthat sendsSpanStarton creation- When the guard drops, sends
SpanEnd - Events are sent via lock-free MPSC channel
- Window thread drains events into a fixed-size ringbuffer
- 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:
# 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)
Description
Languages
Rust
98.9%
Nix
1.1%