Reflex
A fast, GPU-accelerated CPU pipeline trace visualizer built with GPUI (Zed’s rendering framework).
Reflex displays instruction execution timelines, queue occupancy, performance counters, and pipeline stage progression from CPU simulator traces.
Features
- Pipeline visualization — GPU-rendered instruction timeline with smooth panning, zooming, and scrolling at 60fps
- Performance counters — Sparklines, heatmap overview, and numeric display for 200+ hardware counters
- Minimap — Full-trace overview with draggable range selection and pipeline position indicator
- Multicursor — Place multiple cursors to measure cycle deltas between pipeline events, with undo/redo
- Queue panels — Live retire, dispatch, and issue queue occupancy at the cursor position
- Timeline overlay — Counter sparkline strip above the pipeline view for correlation
- Tabbed interface — Open multiple traces side-by-side with independent viewport state
- Konata & µScope formats — Native support for Konata text traces and µScope binary traces
- macOS & Linux — Native builds for both platforms
Quick Start
git clone --recurse-submodules <repo-url>
cargo run --release -- path/to/trace.uscope
Or drag and drop trace files onto the window.
Getting Started
Prerequisites
- Rust (stable toolchain)
- macOS or Linux (GPUI supports both)
- Linux requires additional system libraries (see Building from Source)
Clone and Build
git clone --recurse-submodules https://github.com/zarubaf/reflex.git
cd reflex
cargo build --release
The --recurse-submodules flag is required because the µScope crate is included as a git submodule.
Opening a Trace
# Open a trace file directly
cargo run --release -- path/to/trace.uscope
# Start with an empty window
cargo run --release
You can also:
- Drag and drop trace files onto the Reflex window
- Use Cmd+O to open a file dialog
- Use Cmd+G to generate a random test trace
First Steps
- Open a trace file — the pipeline view shows instruction execution timelines
- Scroll to pan, Ctrl+Scroll to zoom
- Click on the timeline to place the cursor and select an instruction
- Press Cmd+I to see trace metadata (format, clock, duration)
- Switch to the Counters tab to see performance counter data
- Press ? to see the full keyboard shortcut reference
Pipeline Viewer
The pipeline viewer is the main view in Reflex, showing instruction execution timelines as colored stage spans.
Layout
The pipeline viewer has three areas:
- Label pane (left) — Instruction addresses and disassembly, with a resizable splitter
- Timeline pane (right) — Canvas-rendered pipeline stages with cycle ruler header
- Queue panels (bottom/left/right dock) — Retire, dispatch, and issue queue state
Stage Rendering
Each instruction occupies one row. Pipeline stages are rendered as colored rectangles:
- Each stage has a unique color based on its position in the pipeline
- Stage names (e.g.,
Al,Ds,Is,RdEx,Cp) are shown inside the rectangles when zoomed in - At low zoom levels, stages are rendered as thin colored bars for performance
Zoom and Pan
| Action | Effect |
|---|---|
| Scroll / Trackpad | Pan horizontally and vertically |
| Ctrl + Scroll | Zoom in/out (both axes, preserving aspect ratio) |
| Cmd + = / Cmd + - | Zoom in / out |
| Cmd + 0 | Zoom to fit (show entire trace) |
| Arrow keys | Pan in the corresponding direction |
Zoom uses a focal-point model: the point under the cursor stays fixed while the view scales around it. Both horizontal (cycles) and vertical (rows) axes zoom together.
Row Selection
Click on an instruction row to select it. The selected row is highlighted, and its details appear in the queue panels and status bar.
- j / k — Select next / previous instruction
- The status bar shows: instruction count, cycle range, zoom level, selected instruction address, and cursor position
Tooltips
Hover over an instruction to see its annotations as a tooltip. Annotations come from the trace source and typically include operand details, execution metadata, and pipeline events.
Click vs Drag
- Click (press + release without movement) — Selects the instruction row and places the cursor at the clicked cycle
- Drag (press + move) — Pans the viewport without moving the cursor
This distinction prevents accidental cursor jumps during navigation.
Navigation & Cursors
Cursors
Reflex supports multiple cursors for measuring cycle distances between pipeline events.
Placing a Cursor
Click on the timeline to place the active cursor at that cycle. The cursor appears as a vertical line with a labeled head showing the cycle number.
Multicursor
| Key | Action |
|---|---|
| Cmd+M | Add a new cursor at the current position |
| Cmd+Shift+M | Remove the active cursor |
| [ | Switch to previous cursor |
| ] | Switch to next cursor |
Each cursor has a distinct color from a built-in palette. When multiple cursors are placed, the header shows delta values between consecutive cursors.
Cursor Undo/Redo
All cursor operations (moving, adding, removing) are recorded in a history stack:
| Key | Action |
|---|---|
| Cmd+Z | Undo last cursor change |
| Cmd+Shift+Z | Redo |
| Cmd+Y | Redo (alternative) |
The history stores complete cursor state snapshots, so undoing an “add cursor” operation removes the cursor entirely, and undoing a “remove” restores it.
History is capped at 100 entries. New cursor movements clear the redo stack.
Search
Press Cmd+F to open the search bar. Search matches against instruction disassembly text (addresses and mnemonics).
Go to Cycle
Press Cmd+L to open the “Go to Cycle” bar. Enter a cycle number to jump the viewport to that position.
Tab Navigation
| Key | Action |
|---|---|
| Ctrl+Tab | Next tab |
| Ctrl+Shift+Tab | Previous tab |
| Cmd+W | Close current tab |
Each tab has its own independent trace, viewport state, and cursor positions.
Performance Counters
Reflex can parse and display performance counters from µScope traces. Counters track metrics like committed instructions, cache misses, branch mispredictions, and stall cycles.
How Counters Are Detected
Counters are automatically detected from the µScope trace schema. A storage is recognized as a counter if it has:
- Exactly 1 slot (dense, not sparse)
- A single
U64field - Values updated via
DA_SLOT_ADD(incremental additions)
Common counters include committed_insns, cycles, retired_insns, mispredicts, dcache_misses, icache_misses.
Counter Panel
Switch to the Counters tab (next to Pipeline Viewer) to see the counter panel. It has two view modes, toggled by clicking the mode button in the header.
Detail Mode
Shows each counter as a row with:
- Mode indicator (T/R/D) — click to cycle between display modes
- Counter name
- Value at cursor — numeric value at the current cursor position
- Sparkline — min-max envelope visualization below each counter
Heatmap Mode
Shows all counters as a compact matrix:
- One row per counter (~6px per row)
- Color intensity = per-cycle delta activity
- Per-counter normalization (each row’s max maps to full brightness)
- Counter names shown when rows are tall enough
The heatmap is designed for scanning 200+ counters at a glance to spot activity patterns and anomalies.
Display Modes
Click the mode indicator (T/R/D) on any counter row to cycle through:
| Mode | Label | Description |
|---|---|---|
| Total | T | Raw cumulative value |
| Rate | R | Delta per cycle over a 64-cycle window (e.g., IPC) |
| Delta | D | Single-cycle change |
Counter Range
The counter panel displays data for the counter range, which is independent from the pipeline viewport. By default, the counter range covers the full trace. Use the minimap handles to narrow the range to a region of interest.
This independence means you can view full-trace IPC trends in the counter panel while the pipeline view is zoomed into a specific region for detailed inspection.
Konata Traces
Konata format traces do not include performance counters. The counter panel will show “No performance counters in this trace” for Konata files.
Minimap
The minimap is a persistent strip above the main view showing the full trace duration with a counter trendline and navigation controls.
Layout
The minimap shows:
- Counter trendline — filled bars showing one counter’s per-cycle deltas across the entire trace
- Counter range handles — draggable pill-shaped handles controlling which range the counter panel displays
- Dimmed regions — areas outside the counter range are darkened
- Pipeline indicator — subtle yellow bar at the bottom showing where the pipeline viewport is positioned
- Cursor marker — vertical line at the active cursor position
Counter Range vs Pipeline Viewport
The minimap manages two independent concepts:
| Element | Controls | Visual |
|---|---|---|
| Handles (blue pills) | Counter panel range | Blue border rectangle with draggable edges |
| Yellow bar (bottom) | Pipeline viewport position | Read-only indicator |
The counter range defaults to the full trace. Use the handles to narrow it. The pipeline viewport is controlled by scrolling/zooming in the pipeline view.
Interactions
Dragging
- Drag handle body — Pan the counter range (preserves width)
- Drag left/right handle — Resize the counter range
- Scroll wheel — Zoom the counter range in/out (centered on mouse position)
Clicking
- Click anywhere (not on a handle) — Centers the pipeline viewport on the clicked cycle and scrolls vertically to show instructions active at that cycle
- Clicking does NOT move the cursor — cursors are only moved by clicking in the pipeline view
Click vs Drag Detection
The minimap distinguishes between clicks (< 4px movement) and drags (>= 4px movement). Dragging the handles does not trigger a pipeline jump.
Trendline
The minimap displays one counter as a trendline. By default, it shows the first counter in the trace (typically committed_insns). The trendline uses min-max envelope downsampling:
- Data is cached and only recomputed when the counter, trace, or canvas width changes
- Inside the counter range: brighter fill
- Outside the counter range: dimmer fill
Timeline Overlay
The timeline overlay shows a selected counter’s sparkline directly above the pipeline stages, synchronized with the pipeline view’s scroll and zoom.
Toggle
Press Cmd+Shift+O to toggle the overlay on/off.
When enabled, a 30px strip appears between the pipeline header and the stage content. The pipeline stages shift down to accommodate it.
Display
The overlay shows:
- Min-max envelope bars of the selected counter’s per-cycle deltas
- Data synchronized with the pipeline viewport (same visible cycle range)
- Subtle background distinguishing it from the pipeline header
- Rates recompute automatically at the current zoom level
Use Case
The overlay lets you correlate counter behavior with pipeline events without switching to the Counters tab. For example:
- Watch IPC drop while inspecting a cache miss stall in the pipeline
- See branch misprediction spikes aligned with flush events
- Monitor dispatch queue pressure alongside instruction flow
Counter Selection
The overlay shows the first counter by default. The selected counter can be configured via the overlay_counter state (currently toggled via the action, which cycles the first counter on/off).
Queue Panels
The queue panels show the state of the CPU’s internal queues at the current cursor cycle. They update in real-time as you move the cursor.
Queue Types
Retire Queue
Shows the retire buffer (ROB) occupancy. Each slot shows:
- Slot index (hex)
- Current pipeline stage
- Instruction disassembly
The header shows Retire Queue (occupied/total) @ cycle N.
Dispatch Queues
Shows dispatch queue entries grouped by queue instance. Each entry shows:
- Instruction disassembly
- Wait time in cycles since entering the dispatch stage
Queue names come from the µScope DUT property cpu.dispatch_queue_names.
Issue Queues
Shows issue queue entries with ready/waiting status:
- Green dot = ready to issue (all operands available)
- Red dot = waiting (operands pending)
Each entry shows instruction disassembly and wait time. The header shows ready/total counts.
Data Source
Queue data is NOT pre-computed — it’s calculated on-the-fly from the pipeline trace at the cursor cycle. The queue panel reads metadata from the µScope trace:
| DUT Property | Purpose |
|---|---|
cpu.retire_queue_size | Number of ROB slots (default: 128) |
cpu.dispatch_queue_stages | Stage names that represent “in dispatch queue” |
cpu.dispatch_queue_names | Display names for dispatch queue instances |
cpu.issue_queue_stages | Stage names that represent “in issue queue” |
cpu.issue_queue_names | Display names for issue queue instances |
cpu.retire_queue_stages | Stage names that represent “in retire queue” |
Layout
Queue panels are docked in a configurable position:
| Key | Layout |
|---|---|
| Alt+Cmd+1 | Bottom (horizontal split) |
| Alt+Cmd+2 | Left (vertical split) |
| Alt+Cmd+3 | Right (vertical split) |
Toggle queue panel visibility with Cmd+B.
The Issue Queue panel shares a tab bar with the Log panel.
Configuration
Counter Presets
Counter display preferences can be saved in a TOML configuration file. Reflex searches for counters.toml in the following order:
- Next to the trace file (e.g.,
trace.counters.toml) - In the same directory as the trace file (
counters.toml) - User config directory:
- macOS:
~/Library/Application Support/reflex/counters.toml - Linux:
~/.config/reflex/counters.toml
- macOS:
If no config file is found, Reflex uses defaults (show all counters, no overlay).
File Format
[presets.performance]
name = "Overall Performance"
counters = ["committed_insns", "cycles"]
overlay = ["committed_insns"]
[presets.performance.display_modes]
committed_insns = "rate"
cycles = "total"
[presets.cache]
name = "Cache Analysis"
counters = ["dcache_misses", "icache_misses"]
overlay = []
[presets.cache.display_modes]
dcache_misses = "delta"
icache_misses = "delta"
[presets.branch]
name = "Branch Prediction"
counters = ["mispredicts", "committed_insns"]
overlay = ["mispredicts"]
[presets.branch.display_modes]
mispredicts = "rate"
Preset Fields
| Field | Type | Description |
|---|---|---|
name | String | Human-readable preset name |
counters | Array | Counter names to show (empty = show all) |
display_modes | Table | Per-counter display mode: "total", "rate", or "delta" |
overlay | Array | Counter names to show as timeline overlay (empty = no overlay) |
Error Handling
If the config file contains syntax errors, Reflex logs a warning to stderr and falls back to defaults. The application never crashes due to a malformed config file.
Layout Presets
The queue panel layout is switchable via keyboard shortcuts:
| Key | Layout |
|---|---|
| Alt+Cmd+1 | Bottom dock |
| Alt+Cmd+2 | Left dock |
| Alt+Cmd+3 | Right dock |
Layout state is not persisted between sessions.
Keyboard Shortcuts
Viewport
| Key | Action |
|---|---|
| Scroll / Trackpad | Pan |
| Ctrl + Scroll | Zoom in/out |
| Cmd + = / Cmd + + | Zoom in |
| Cmd + - | Zoom out |
| Cmd + 0 | Zoom to fit |
| Left / Right / Up / Down | Pan |
Selection
| Key | Action |
|---|---|
| j | Select next instruction |
| k | Select previous instruction |
| Click (timeline) | Select row + place cursor |
Cursors
| Key | Action |
|---|---|
| Cmd + M | Add cursor |
| Cmd + Shift + M | Remove active cursor |
| [ | Previous cursor |
| ] | Next cursor |
| Cmd + Z | Undo cursor change |
| Cmd + Shift + Z | Redo cursor change |
| Cmd + Y | Redo cursor change |
Search & Navigation
| Key | Action |
|---|---|
| Cmd + F | Toggle search |
| Cmd + L | Go to cycle |
Files & Tabs
| Key | Action |
|---|---|
| Cmd + O | Open file |
| Cmd + R | Reload current trace |
| Cmd + W | Close tab |
| Ctrl + Tab | Next tab |
| Ctrl + Shift + Tab | Previous tab |
| Cmd + G | Generate random trace |
Panels & Layout
| Key | Action |
|---|---|
| Cmd + B | Toggle queue panels |
| Cmd + I | Toggle info overlay |
| ? | Toggle help overlay |
| Cmd + Shift + O | Toggle counter overlay |
| Alt + Cmd + 1 | Queue layout: bottom |
| Alt + Cmd + 2 | Queue layout: left |
| Alt + Cmd + 3 | Queue layout: right |
Other
| Key | Action |
|---|---|
| Alt + Cmd + W | Connect WCP |
| Alt + Cmd + Shift + W | Disconnect WCP |
| Cmd + Q | Quit |
Trace Formats
Reflex supports two trace formats. The format is auto-detected from the file’s magic bytes.
µScope (.uscope)
Binary format from µScope with CPU protocol semantics.
Features:
- LZ4 compression
- Checkpointed random access via segments
- Performance counters (automatically detected)
- Queue metadata (retire/dispatch/issue queue configuration)
- Pipeline stage names from schema
- Instruction annotation via string table
- RISC-V instruction decoding (RV64GC)
- Clock domain information
DUT Properties used by Reflex:
| Property | Purpose |
|---|---|
cpu.pipeline_stages | Pipeline stage names (comma-separated) |
cpu.retire_queue_size | Retire buffer slot count |
cpu.dispatch_queue_stages | Stage names for dispatch queue membership |
cpu.dispatch_queue_names | Display names for dispatch queues |
cpu.issue_queue_stages | Stage names for issue queue membership |
cpu.issue_queue_names | Display names for issue queues |
cpu.retire_queue_stages | Stage names for retire queue membership |
Trace metadata shown in the info overlay (Cmd+I):
- File name, format version, flags
- Clock domain (name, period, frequency)
- Pipeline stage sequence
- Duration (cycles and microseconds)
- Segment count, schema summary, string table size
Konata (.log, .konata, .kanata)
Text-based Kanata log format used by CPU simulators. Compatible with Konata.
Features:
- Human-readable text format
- Stage transitions, labels, dependencies
- Flush/squash events
Limitations compared to µScope:
- No performance counters
- No queue metadata
- No compression or random access
- No instruction decoding (uses labels from the trace)
Generating Test Traces
Press Cmd+G to generate a random synthetic trace for testing. The generator creates a configurable number of instructions with realistic pipeline stage patterns, dependencies, and timing.
Building from Source
Prerequisites
- Rust stable toolchain (install)
- Git with submodule support
macOS
No additional dependencies needed. GPUI uses native Metal rendering.
Linux
Install the following system libraries:
sudo apt-get install -y \
libwayland-dev \
libxkbcommon-x11-dev \
libvulkan-dev \
libfontconfig1-dev \
libfreetype6-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
libxcb-cursor-dev \
libxcb-render0-dev \
libxcb-randr0-dev \
libxcb-keysyms1-dev \
libxcb-xkb-dev \
libxkbcommon-dev \
libglib2.0-dev \
libatk1.0-dev \
libgtk-3-dev \
libclang-dev \
cmake
Clone
git clone --recurse-submodules https://github.com/zarubaf/reflex.git
cd reflex
If you already cloned without --recurse-submodules:
git submodule update --init --recursive
Build
cargo build # Debug build
cargo build --release # Release build (recommended for large traces)
Run
cargo run --release -- path/to/trace.uscope
Format and Lint
cargo fmt # Format code
cargo clippy # Lint
cargo test # Run tests
CI
The project uses GitHub Actions for CI:
- Build — macOS and Linux release builds
- Clippy — Lint with
-D warnings - Format —
cargo fmt --check - Test —
cargo test
All CI jobs use actions/checkout@v4 with submodules: recursive to handle the µScope submodule.
Release
Tagged releases (v*) trigger the release workflow:
- macOS:
.appbundle, signed and notarized - Linux: standalone binary tarball
- Both uploaded as GitHub release artifacts