with one click
elodin-tracy
// Profile Elodin with Tracy. Use when profiling the editor, simulation, building with tracy features, capturing traces, analyzing performance, or adding custom instrumentation.
// Profile Elodin with Tracy. Use when profiling the editor, simulation, building with tracy features, capturing traces, analyzing performance, or adding custom instrumentation.
Develop and contribute to the Elodin codebase. Use when building Elodin from source, running tests, modifying core libraries, working on the Rust workspace, or onboarding as a contributor.
Contribute to the Elodin Editor, the 3D viewer and graphing tool. Use when editing files in libs/elodin-editor/ or apps/elodin/, working on the Bevy/Egui UI, modifying viewport rendering, telemetry graphs, video streaming, KDL schematics, or the command palette.
Tips for working with a Bevy application
Deploy and configure AlephOS on flight computers, write flight software services, and manage NixOS modules for the Aleph platform. Use when working with aleph/, deploying to Jetson Orin hardware, writing NixOS modules, flashing firmware, or composing a flight software stack.
Work with the Cranelift JIT MLIR backend. Use when modifying libs/cranelift-mlir/, adding new StableHLO ops, debugging simulation correctness issues, running the checkpoint diagnostic tool, or working on the pointer-ABI tensor runtime.
Create and modify physics simulations using the Elodin Python SDK. Use when writing or editing simulation Python files, defining components or systems, spawning entities, configuring 6DOF physics, setting up visualization, or integrating with SITL/HITL workflows.
| name | elodin-tracy |
| description | Profile Elodin with Tracy. Use when profiling the editor, simulation, building with tracy features, capturing traces, analyzing performance, or adding custom instrumentation. |
Tracy is a real-time, nanosecond-resolution hybrid frame and sampling profiler. https://github.com/wolfpld/tracy | BSD 3-clause
Linux only. Tracy profiling requires Linux. On macOS, just install tracy will abort with an explanation. Use a Linux machine or an OrbStack NixOS VM (see the elodin-nix skill) for profiling workflows.
nix develop
just install tracy
elodin editor examples/sensor-camera/main.py
In a second terminal:
tracy # launches the Tracy profiler GUI
Click Connect in the Tracy UI. You will see both the editor process (Bevy systems, UI rendering) and the simulation subprocess (IREE kernel execution) as separate clients.
For full sampling and context-switch data on Linux, run the profiled binary with elevated privileges:
sudo elodin editor examples/sensor-camera/main.py
Each Elodin process uses a dedicated Tracy port so they can be profiled independently or simultaneously:
| Process | Tracy Port |
|---|---|
Editor / elodin run | 8087 |
| Render server | 8088 |
| Simulation (IREE) | 8089 |
| Elodin-DB | 8090 |
When using the Tracy GUI, connect to each port separately. When using tracy-capture, specify the port with -p.
To capture traces headlessly (useful for CI, remote machines, or agentic workflows):
# Editor + render-server (Tracy v0.13.x protocol):
tracy-capture -a 127.0.0.1 -p 8087 -o /tmp/trace-editor.tracy -s 30 &
tracy-capture -a 127.0.0.1 -p 8088 -o /tmp/trace-render.tracy -s 30 &
sleep 1
source .venv/bin/activate
elodin editor examples/sensor-camera/main.py
# After capture completes, export to CSV for analysis:
tracy-csvexport /tmp/trace-editor.tracy > /tmp/trace-editor.csv
tracy-csvexport /tmp/trace-render.tracy > /tmp/trace-render.csv
tracy-csvexport /tmp/trace-sim.tracy > /tmp/trace-sim.csv
Start the capture before launching Elodin so the servers are listening when the Tracy clients initialize. The -s 30 flag records for 30 seconds; adjust as needed.
You can also open saved .tracy files in the GUI later:
tracy /tmp/trace-editor.tracy
When the tracy feature is enabled, the editor binary (apps/elodin) sets up a tracing_tracy::TracyLayer in its tracing subscriber (apps/elodin/src/cli/mod.rs). This means every Rust tracing span becomes a Tracy zone.
Automatic (zero code changes):
bevy/trace_tracy)#[tracing::instrument]Tracy-specific runtime behavior:
AutoNoVsync (eliminates vsync idle from profiles)continuous() mode instead of reactive/game modeWhen cranelift-mlir is built with --features tracy (propagated via nox-py/tracy from just install tracy), each JIT-compiled function emits a Tracy zone named after its FuncId → name mapping (e.g. main, inner_929, svd). The zones appear in the same sim-subprocess Tracy port (8089) alongside the existing sim instrumentation.
Activation requires both:
--features tracy (or just install tracy)ELODIN_CRANELIFT_DEBUG_DIR=<path>Without ELODIN_CRANELIFT_DEBUG_DIR, the Cranelift JIT IR emits no probe calls, so Tracy produces zero zones for JIT'd functions — the feature is runtime-toggled orthogonally to the Cargo feature. See libs/cranelift-mlir/PERFORMANCE.md for the full workflow.
When built with --features tracy, the elodin-db binary (libs/db/src/main.rs) adds a TracyLayer to its tracing subscriber on port 8090. Instrumented hot paths:
handle_conn -- per-connection lifetimehandle_packet -- per-packet dispatchsink_table -- table decomposition + write-lock acquisition (the primary write path)apply_value (trace-level) -- per-component write within a tablepush_buf (trace-level) -- mmap append to index + data filesfollow_stream -- follow/replication egress pathcoalescing_flush (trace-level) -- TCP write coalescingThe apply_value and push_buf spans use trace_span! to minimize overhead at default log levels. Set RUST_LOG=trace or connect Tracy to capture them.
A throughput benchmark is available:
# Customer scenario: 400 components at 250Hz, per-component connections, with a reader
elodin-db-bench --scenario customer --json
# Same workload but batched into single-table packets (faster)
elodin-db-bench --scenario customer --mode batch --json
# Custom configuration
elodin-db-bench --components 1000 --frequency 100 --duration 20 --mode per-component
just install tracy doesnox-py (Python extension) with maturin develop -F tracyelodin editor and elodin-db binaries with cargo build --release -p elodin -p elodin-db --features tracy, which activates bevy/trace_tracy and adds tracing-tracy to both processesapps/elodin tracy = ["elodin-editor/tracy", "bevy/trace_tracy", "dep:tracing-tracy"]
libs/elodin-editor tracy = ["bevy/trace_tracy"]
libs/db tracy = ["dep:tracing-tracy"] (adds TracyLayer to subscriber, port 8090)
libs/nox-py tracy = ["cranelift-mlir/tracy"] (forwards to JIT profiling layer)
libs/cranelift-mlir tracy = ["dep:tracy-client"] (emits per-JIT-function zones, port 8089)
Any tracing span automatically appears in Tracy when the tracy feature is enabled:
#[tracing::instrument]
fn my_hot_function() {
// entire function is a Tracy zone
}
fn partial_instrumentation() {
let _span = tracing::info_span!("critical_section").entered();
// only this block is a Tracy zone
}
The current EnvFilter (s10=info,elodin=info,impeller=info,...) controls which spans reach Tracy. To capture more detail:
RUST_LOG=debug elodin editor examples/sensor-camera/main.py
#[tracing::instrument(skip(graph))]
fn my_hot_function() {
// Entire function is a Tracy zone.
}
The `skip(graph)` option tells [`#[tracing::instrument]`](https://docs.rs/tracing/0.1/tracing/attr.instrument.html) not to attach the `graph` argument as a span field. By default the macro would try to record every parameter (usually via `Debug`), which is noisy for large values, can fail if a type has no useful `Debug`, and is rarely needed when you only want a named zone in Tracy.
---
## 6. Tips and Troubleshooting
### Tracy won't connect
- Ensure the profiler UI is running and listening **before** starting the Elodin binary
- Or set `TRACY_NO_EXIT=1` to keep the app alive until Tracy connects:
```bash
TRACY_NO_EXIT=1 elodin editor examples/sensor-camera/main.py
Sampling and context-switch capture require elevated privileges on Linux:
sudo elodin editor examples/sensor-camera/main.py
If you see the Tracy timeline but no ghost zones or CPU core list, this is the cause.
Tracy keeps many file descriptors open. Increase the limit:
sudo sh -c "ulimit -n 65536 && elodin editor examples/sensor-camera/main.py"
On some Linux systems, GPU and CPU timelines drift due to network time sync:
sudo systemctl stop systemd-timesyncd
# Re-enable when done:
sudo systemctl start systemd-timesyncd
With Tracy enabled, Bevy's RenderDiagnosticsPlugin requires a DiagnosticsStore resource. The headless render server disables DiagnosticsPlugin but now explicitly initializes DiagnosticsStore to prevent panics (libs/elodin-editor/src/headless.rs).
tracy-profiler [file.tracy] # Open saved trace
tracy-profiler -a 127.0.0.1 [-p 8086] # Auto-connect to address
tracy-capture -o out.tracy [-a addr] [-p port] [-f] [-s seconds] [-m mem%]
tracy-csvexport trace.tracy [-f name] [-c] [-s sep] [-e] [-u] > out.csv
Columns: name, src_file, src_line, total_ns, total_perc, counts, mean_ns, min_ns, max_ns, std_ns.
tracy-update old.tracy new.tracy [-4|-h|-e|-z level] [-j streams] [-d] [-c] [-r] [-s flags]
Strip flags: locks messages plots Memory images ctx-switches sampling Code Source-cache.
All defined project-wide. In Elodin, these are managed by the tracy-client-sys crate (editor).
TRACY_ENABLE -- required; without it all macros are no-opsTRACY_ON_DEMAND -- profile only when server is connected (saves memory)TRACY_NO_EXIT -- wait for server before exiting (also env var)TRACY_NO_BROADCAST -- no UDP presence announcementTRACY_ONLY_LOCALHOST -- localhost only (also env var)TRACY_PORT -- data+broadcast port (default 8086; also env var)TRACY_NO_SYSTEM_TRACING -- no kernel data (also env var)TRACY_NO_CONTEXT_SWITCH -- no context switch captureTRACY_NO_SAMPLING -- no call stack samplingTRACY_NO_CALLSTACK -- no call stack support at allTRACY_NO_CODE_TRANSFER -- no executable code retrievalTRACY_FIBERS -- fiber/coroutine support (small perf hit)TRACY_CALLSTACK=<depth> -- force callstack capture on all macrosTRACY_SAMPLING_HZ=<freq> -- sampling frequency (default 10 kHz Linux)| Key | Action |
|---|---|
A/D | Scroll left/right |
W/S | Zoom in/out |
Ctrl+F | Find Zone |
Ctrl+drag | Define time range |
Ctrl+click zone | Zone statistics |
| Middle-click | Zoom to extent |
| Left-click zone | Zone info |
| Right-click zone | Set time range |
Ctrl+Alt+R | Reconnect (live) |
Hard limits: 64 threads/lock, 65534 source locations, 255 recursive zone appearances, 1.6-day max session, 4B memory frees, 16M unique callstacks. Little-endian only, 48-bit VA.
Pitfalls:
exit() inside a zone = hang. Use exception workaround.Free needs matching Alloc. Mismatch kills session.TRACY_ON_DEMAND, events buffer unbounded in RAM.Linux notes:
perf_event_paranoid set to -1.--privileged --pid=host --user 0:0, mount /sys/kernel/debug.