2025-12-17 08:33:22 +01:00
2025-12-11 00:12:16 +01:00
2025-12-17 01:08:59 +01:00
2025-12-10 23:50:28 +01:00
2025-12-10 23:50:28 +01:00
2025-12-11 00:12:16 +01:00
2025-12-11 00:12:16 +01:00
2025-12-17 00:36:21 +01:00
2025-12-17 08:33:22 +01:00

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 ~1200 LOC with minimal dependencies.

Features

  • Unified thread tracks with expandable call stacks (click headers to toggle)
  • Collapsed view shows when threads are active (easy to spot blocking)
  • Expanded view shows full call stack hierarchy with flame graph visualization
  • Ongoing span support for long-running functions (main loops, render threads)
  • Pause mechanism to freeze your application for inspection (Space bar)
  • Monokai color palette for easy visual distinction
  • Incremental tree building - only processes new spans each frame
  • Ringbuffer storage (~16MB, 1M events) for recent history
  • Lock-free event recording via MPSC channels

Dependencies

Only 7 direct dependencies (~70 total including transitive):

  • minifb - Window and framebuffer
  • crossbeam-channel - Lock-free MPSC
  • once_cell - Lazy statics
  • fontdue - Font rasterization
  • procmacro2, syn, quote - Proc macros

Usage

Add to your Cargo.toml:

[dependencies]
teleprof = { path = "../teleprof" }

In your code:

fn main() {
    // Start the profiler window (separate thread)
    #[cfg(debug_assertions)]
    teleprof::start();
    
    // Name your thread (optional, shows in UI)
    #[cfg(debug_assertions)]
    teleprof::set_thread_name("main");
    
    game_loop();
}

fn game_loop() {
    loop {
        teleprof::span!("main_frame");
        
        update();
        render();
    }
}

fn update() {
    teleprof::span!("update");
    // Your update code
}

fn render() {
    teleprof::span!("render");
    // Your render code
}

Controls

  • Space: Toggle pause (freezes ongoing spans at current time)
  • Left click + drag: Box select to zoom (click background not function)
  • Right click + drag: Pan timeline
  • Scroll: Zoom timeline horizontally
  • Click track header: Expand/collapse thread's call stack
  • 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. Incrementally builds per-thread call trees (only processes new spans)
  6. Renders unified thread tracks with expandable call stacks

Design Goals

  • Minimal overhead: Lock-free event recording, incremental tree building
  • 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
  • Handle any thread pattern: Long-lived, short-lived, thread pools (Rayon, etc.)

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

???

Description
No description provided
Readme 348 KiB
Languages
Rust 98.9%
Nix 1.1%