Debugging Playbook
- Logging: Set
RUST_LOG=orbitron=debug(or use the CLI--log-level debug) to enable verbose tracing. The viewer records structured events for background jobs, and selection evaluation. - GPU issues: Launch with
WGPU_BACKEND=gl conda run -n orbitron-dev cargo run --release -p orbitron-cli --features gui -- view ...to force the OpenGL backend when Metal/Vulkan is unavailable. - Loader problems: Use the CLI
inspectandconvertsubcommands to reproduce parsing issues outside the GUI. The regression crate’sload_and_digesthelper is useful for verifying canonical results on fixtures. - Edit mode glitches:
viewer_state::attempt_exit_edit_modeandedit_exit_dialog::handle_exit_edit_dialogcontrol edit-mode transitions. Enable tracing around these modules to observe undo/redo stack operations. - Background tasks: Progress overlays pull from
ProgressMsgchannels (ui/shell/src/background/types.rs). Ensure new background jobs send a terminal variant —ProgressMsg::Done, or the matchingDone*Summaryfor a run-summary job — or the spinner will persist. - Serial-vs-parallel timing:
RAYON_NUM_THREADS=1 cargo bench -p orbitron-services --bench orbital_grid_evalruns the Rayon-parallel orbital evaluator on a single worker, which approximates the pre-parallel serial loop (within ~11% at 96³ on an M2 Ultra). For the exact serial baseline, restorecore/services/src/analysis/orbitals/eval/grid.rsfrom the parent of commit25b5b129and rerun; see the bench header comment.
Diagnostics: logging, crash reports, and the memory watchdog
When the desktop viewer misbehaves — a panic, a wgpu error, a runaway load — the evidence lands in one of three places, all wired together so a crash report is self-contained.
Logging and the Console panel
Logging is tracing. The subscriber lives in ui/shell/src/runner.rs and fans out to two sinks (ui/shell/src/console_log.rs):
- a console buffer (INFO and above) shown live in the viewer’s Console panel — click Console at the bottom-left of the window;
- a trace ring (DEBUG/TRACE from the
orbitron*crates) kept only in memory for the crash file.
stderr additionally receives WARN and above by default; RUST_LOG overrides the stderr filter (RUST_LOG=info, RUST_LOG=orbitron=debug, per-module directives). Note that a bundled .app launched by double-click has no visible stderr — read the in-app Console panel, read the crash file, or launch the binary directly from a terminal to see the stream.
Events worth knowing are logged at INFO so they survive in the trace ring and the crash file:
- the selected GPU adapter (name, backend, device type) at renderer init;
- every file open (
session/load.rs) with its path and size; - every background task-segment load (
background/task_segment.rs) — a start line with the file size and task kind, and a finish line with elapsed time and success. These are the breadcrumb for load-time hangs and memory spikes.
Crash reports
Any panic runs the hook in ui/shell/src/crash_reporter.rs, which writes a crash-<unix-ts>.log to the platform data directory:
macOS ~/Library/Application Support/dev.Orbitron.Orbitron/crashes/
Linux ~/.local/share/orbitron/crashes/
Each report contains, in order:
- a header — Orbitron version, OS/arch, and the file that was open;
- the panic thread, source location, and message;
- a full backtrace (
Backtrace::force_capture, so it fills in regardless ofRUST_BACKTRACE) — this is what turns a symptom location likemesh/pipeline.rs:239into the call chain that reached it; - the recent trace ring (DEBUG/TRACE) and console buffer (INFO+) leading up to the crash.
On the next launch check_for_crash loads the most recent report into a banner in the Console panel, then deletes the file — the report is shown once, so copy it from the banner (or read it from the folder above) before dismissing. Help → Open Crash Reports Folder (MenuAction::OpenCrashReportsFolder → crash_reporter::reveal_crash_dir) opens that directory in the OS file manager, creating it if needed.
report_header is factored out so its format is unit-tested; if you extend the header, update that test.
Memory watchdog
ui/shell/src/mem_watchdog.rs samples current process RSS and host memory every three seconds. Notice and Critical bands are relative to installed memory and current headroom. A five-sample window also detects uninterrupted growth; three clear samples rearm the warning after recovery. This makes the same workload warn earlier on an 8 or 16 GiB machine than on a 192 GiB workstation.
Each state change records the active operation, current RSS, observed peak RSS, total memory, available memory, and sustained growth in the trace log. The watchdog wakes the event loop and sends the same state to a desktop toast instead of logging alone. It never terminates the process or replaces the current scene. File and task loads expose a Cancel button because those workers have real cancel handles; jobs without a safe cancellation path do not show a misleading button.
Memory guardrail (capacity gate)
core/services/src/capacity/ is the shared pre-flight gate. project(op: &Operation, snap: &MemorySnapshot, cap: Option<HardCap>) -> Verdict is pure and unit-tested (band logic: Ok <70% / Warn 70–100% / Critical >100% of available RAM; an overridable Block above physical+swap; an enforced Block only for a hard cap). CapacityProbe wraps sysinfo (0.38, pinned for the 1.92 toolchain) — total+swap cached at startup, available re-read per gate. Each frontend calls the gate at its heavy entry points and applies the verdict: the desktop shows a confirm dialog (ui/shell/src/capacity_gate.rs), the CLI warns/prompts (automation/cli’s memory_gate), Python emits warnings.warn / raises MemoryError. Projections are logged (tracing, “memory gate: projection” / “…strain memory”) so a tuning benchmark can pair them with actual peak RSS. The per-format multiplier table is a const in the module; tune it there against the projected-vs-actual log.
GPU (wgpu) errors
orbitron_render::install_gpu_error_logger sets Device::on_uncaptured_error so wgpu validation and out-of-memory errors are logged (target wgpu) instead of the default panic-abort. It is installed on every device Orbitron creates — the main window, the off-screen/headless renderer, and the export window — so a bad draw call leaves a diagnostic line rather than taking down the app. Adapter selection is logged alongside it, so a GPU-specific report names the backend it happened on.