Profiler
Basilisk includes a fully integrated Python profiler. CPU hotspot heatmaps appear inline on every line of your code, memory leaks are flagged as diagnostics, and flamegraphs open directly in your editor — without leaving your workspace.
Overview
The Basilisk profiler combines two complementary engines:
- CPU profiling — powered by py-spy, a sampling profiler that attaches to a running Python process with no code changes required. Samples the call stack at a configurable rate (default: 100 Hz) and shows inline heat annotations on every hot line.
- Memory profiling — powered by Python's
tracemallocmodule, injected at runtime. Tracks allocations over time, diffs snapshots to find leaks, and walks reference graphs to identify retention chains.
Both engines are orchestrated through the Basilisk LSP server (Rust). The IDE extensions listen for LSP notifications and render results — no standalone CLI needed.
CPU Profiling
Starting a profiling session
VS Code:
- Run the Python script or test you want to profile (it must be running)
- Open the Command Palette (
Cmd+Shift+P/Ctrl+Shift+P) - Run Basilisk: Start Profiling
- Select the target process from the list
Zed:
Use the slash command in the AI panel:
/profile
Neovim:
:BasiliskProfileStart
Inline heatmap annotations
As samples accumulate, Basilisk annotates every hot line directly in the editor gutter. The heat palette uses four levels:
| Level | Threshold | Colour | What it means |
|---|---|---|---|
| Critical | ≥ 10% of CPU time | 🔴 #e8500a |
Severe bottleneck — optimise first |
| Hot | ≥ 5% | 🟠 #f97316 |
Significant overhead |
| Warm | ≥ 2% | 🟡 #fbbf24 |
Moderate cost, worth reviewing |
| Cool | ≥ 1% | ⚫ #4a5468 |
Minor overhead |
Lines below 1% receive no annotation.
Taking a snapshot
Capture a point-in-time profile while a session is running:
- VS Code: Command Palette → Basilisk: Snapshot Profile
- Zed:
/profsnapshot - Neovim:
:BasiliskProfileSnapshot
Snapshots are saved to the basilisk-profiles/ directory in your workspace as Speedscope-compatible JSON. You can open them in Speedscope for deeper analysis.
Stopping profiling
- VS Code: Command Palette → Basilisk: Stop Profiling (or click the status bar item)
- Zed:
/profstop - Neovim:
:BasiliskProfileStop
Flamegraph viewer
When you stop a session (or take a snapshot), Basilisk opens a flamegraph webview directly in VS Code.
The flamegraph viewer includes:
- Summary cards — total samples, duration, top function, peak thread count
- Interactive flamegraph — click a frame to zoom in; click the breadcrumb to zoom out
- Top functions table — sorted by CPU time, with file/line links that navigate to source
- Export — download the raw Speedscope JSON for external analysis
Attaching to a debug session
You can profile a program that is already running under the Basilisk debugger:
- VS Code: Command Palette → Basilisk: Attach Profiler to Debug Session
This attaches py-spy to the debugpy-managed process, so you can set breakpoints and see profiling data simultaneously.
Profile comparison (diff)
To compare two profiling sessions:
- Take a snapshot of session A (save as baseline)
- Make your change
- Take a snapshot of session B
- Run Basilisk: Compare Profiles and select both snapshots
The diff view highlights functions that got faster (green) or slower (red) between the two sessions.
Memory Profiling
Starting memory tracking
Memory tracking injects Python's tracemalloc module into the running process to record every allocation with its call stack.
- VS Code: Command Palette → Basilisk: Start Memory Tracking
- Zed:
/memleak - Neovim:
:BasiliskMemoryStart
Taking memory snapshots
Capture a snapshot of the current allocation state:
- VS Code: Command Palette → Basilisk: Take Memory Snapshot
- Zed:
/memsnap
Snapshots are numbered sequentially (snapshot 1, snapshot 2, …). Take at least two snapshots to enable diff analysis.
Finding memory leaks
Diff two snapshots to identify allocations that grew between them:
- VS Code: Command Palette → Basilisk: Diff Memory Snapshots
- Zed:
/memdiff
Basilisk compares the snapshots and emits LSP diagnostics on the lines that allocated memory that was not freed. Diagnostics use the BSK-PROF-MEM code and appear as warnings in the Problems panel.
Leak confidence scoring:
| Badge | Score | Meaning |
|---|---|---|
| Definite | 95–100% | Object is reachable only from a leaked root |
| High | 70–94% | Strong retention evidence |
| Medium | 40–69% | Possible leak, needs investigation |
| Low | < 40% | Suspicious growth but uncertain |
Reference graph
The reference graph walks the Python object graph from a suspected leak root to show exactly what is retaining it in memory.
- VS Code: Command Palette → Basilisk: Show Reference Graph
- Zed:
/memrefs
The graph is rendered as an interactive force-directed layout:
- Node size — proportional to the object's memory footprint
- Node colour — object type (dict, list, class instance, module, etc.)
- Edge labels — attribute name, index, or key that holds the reference
- Cycles — highlighted in red; cycles prevent garbage collection
Click any node to inspect its type, repr(), size, and outgoing references.
Memory timeline
The memory dashboard shows allocation growth over time as a stacked area chart, broken down by object type.
Stopping memory tracking
- VS Code: Command Palette → Basilisk: Stop Memory Tracking
- Zed:
/memstop - Neovim:
:BasiliskMemoryStop
Memory Dashboard
The memory dashboard aggregates all memory profiling views into a single panel:
Panels in the dashboard:
| Panel | Description |
|---|---|
| Timeline | Heap growth over the session, stacked by object type |
| Top allocators | Functions responsible for the most live allocations |
| Leak candidates | Ranked list with confidence scores and allocation call stacks |
| Reference graph | Force-directed object graph for the selected leak candidate |
Profiling Presets
Basilisk ships four profiling presets that trade coverage for overhead:
| Preset | Sample rate | Native frames | Memory | Overhead |
|---|---|---|---|---|
default |
100 Hz | No | No | ~1% |
lightweight |
25 Hz | No | No | <0.5% |
detailed |
200 Hz | Yes | No | ~3% |
memory |
50 Hz | No | Yes | ~2% |
Select a preset from the Basilisk settings panel or set it in pyproject.toml:
[tool.basilisk.profiler]
preset = "detailed"
Configuration
All profiler settings live under [tool.basilisk.profiler] in pyproject.toml:
[tool.basilisk.profiler]
enabled = true
sample-rate = 100 # Hz — samples per second
include-native = false # include C extension frames
line-threshold = 1.0 # minimum % to show line annotation
function-threshold = 2.0 # minimum % to show function in table
output-directory = "basilisk-profiles"
| Setting | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable the profiler feature |
sample-rate |
int | 100 |
Sampling frequency in Hz |
include-native |
bool | false |
Include C extension and Rust frames |
line-threshold |
float | 1.0 |
Minimum CPU % for line annotations |
function-threshold |
float | 2.0 |
Minimum CPU % for table entries |
output-directory |
string | "basilisk-profiles" |
Where snapshots are saved |
VS Code settings mirror these under basilisk.profiler.*:
{
"basilisk.profiler.sampleRate": 100,
"basilisk.profiler.includeNative": false,
"basilisk.profiler.lineThreshold": 1.0
}
Platform requirements
macOS
py-spy requires the ability to read the memory of other processes. On macOS, this requires elevated privileges for processes you do not own.
Basilisk ships a privileged helper binary (basilisk-profiler-helper) that handles this transparently. The first time you profile a process, macOS will prompt for your administrator password via a standard system dialog. The helper is code-signed and runs only the specific operations needed.
To profile your own process (e.g. a script you launched from Basilisk), no elevation is required.
Linux
On Linux, ptrace access is governed by /proc/sys/kernel/yama/ptrace_scope:
| Scope | Meaning | Basilisk behaviour |
|---|---|---|
0 |
All processes | No elevation needed |
1 (default on many distros) |
Parent/child only | Basilisk launches a child shim |
2 |
Admin only | Requires sudo |
3 |
Disabled | Profiling unavailable |
If you see a permission error on Linux, check:
cat /proc/sys/kernel/yama/ptrace_scope
To temporarily allow profiling:
sudo sysctl kernel.yama.ptrace_scope=0
Windows
No elevation is required on Windows. The profiler attaches using standard Win32 APIs available to any process owner.
Diagnostic codes
| Code | Severity | Description |
|---|---|---|
BSK-PROF-LINE |
Warning | Line spends ≥ line-threshold% of CPU time |
BSK-PROF-FUNC |
Warning | Function spends ≥ function-threshold% of CPU time |
BSK-PROF-GIL |
Warning | Thread blocked on GIL ≥ 20% of the time |
BSK-PROF-MEM |
Warning | Memory allocation that was not freed between snapshots |
These diagnostics appear in the Problems panel alongside type errors. They are suppressed when no profiling session is active.
Profiling in Zed
The Zed extension surfaces profiling via slash commands in the AI assistant panel. There is no webview in Zed; instead, flamegraph data is exported to the basilisk-profiles/ directory and diagnostics appear inline in the editor.
| Command | Action |
|---|---|
/profile |
Start CPU profiling |
/profstop |
Stop CPU profiling |
/profsnapshot |
Take CPU snapshot |
/memleak |
Start memory tracking |
/memstop |
Stop memory tracking |
/memrefs |
Show reference graph for top leak |
Profiling in Neovim
The Neovim plugin exposes profiling via user commands:
| Command | Action |
|---|---|
:BasiliskProfileStart |
Start CPU profiling |
:BasiliskProfileStop |
Stop CPU profiling |
:BasiliskProfileSnapshot |
Take CPU snapshot |
:BasiliskMemoryStart |
Start memory tracking |
:BasiliskMemoryStop |
Stop memory tracking |
Inline heatmap annotations appear as virtual text in the sign column.
Architecture
┌──────────────┐ LSP (JSON-RPC) ┌──────────────────────────────┐
│ VS Code / │◄────────────────────►│ Basilisk LSP (Rust) │
│ Zed / Nvim │ │ ┌──────────────────────────┐ │
└──────────────┘ │ │ ProfileSessionManager │ │
│ │ Sampler thread (py-spy) │ │
│ │ SampleAggregator │ │
│ │ SpeedscopeExporter │ │
│ │ MemorySessionManager │ │
│ │ SnapshotDiffer │ │
│ │ ReferenceGraphWalker │ │
│ └──────────────────────────┘ │
└──────────────┬───────────────┘
│ attaches to
▼
┌──────────────────┐
│ Python process │
│ (your code) │
└──────────────────┘
The LSP server owns all profiling state. Editor extensions are pure UI — they send commands and render what the LSP sends back via notifications. This means profiling works identically in VS Code, Zed, and Neovim.
Performance targets
The profiler is designed to be low-overhead by default:
| Operation | Target |
|---|---|
| Sampling overhead | < 3% CPU |
| LSP memory for 10-min session | < 50 MB |
| Diagnostics generation | < 100 ms |
| Speedscope export | < 200 ms |
| Flamegraph SVG render | < 500 ms |
Troubleshooting
"Process not found"
The target process exited before the profiler could attach. Make sure your script is still running before starting a profiling session.
"Not a Python process"
py-spy only works with CPython. PyPy and other interpreters are not supported.
"Permission denied" (macOS)
Click Allow on the system dialog that appears, or run VS Code/Zed with administrator privileges. See Platform requirements.
"Permission denied" (Linux)
Check ptrace_scope — see Linux section above.
"Already profiling"
Only one CPU profiling session can run at a time. Stop the current session first.
Snapshots not appearing
Ensure output-directory is writable. By default this is basilisk-profiles/ relative to your workspace root.
Comparison with other profilers
| Feature | Basilisk | cProfile | Scalene | Memray | Austin |
|---|---|---|---|---|---|
| Zero code changes | Yes | No | No | No | Yes |
| Inline editor annotations | Yes | No | No | No | No |
| Memory leak detection | Yes | No | Yes | Yes | No |
| Reference graph | Yes | No | No | No | No |
| IDE integration (native) | Yes | No | No | No | No |
| Flamegraph viewer | Yes | No | Yes | Yes | No |
| Profile on attach | Yes | No | No | No | Yes |
| GIL contention | Yes | No | Yes | No | Yes |
Next steps
- Configuration reference — full
pyproject.tomlschema including profiler settings - Debugging — use the integrated debugger alongside the profiler
- Installation — platform setup including privilege requirements