VS Code profiling on the live debug session (native .cpuprofile/.heapprofile) + zero-config debug start#76
Merged
Merged
Conversation
…g session's process CPU "same process": the debug-adapter tracker captures the debuggee's systemProcessId from debugpy's `process` event into the store; "Profile Debug Session" attaches py-spy to that exact PID (the LSP profiler stays PID-based — no server-side debugSession resolution). The privilege layer handles macOS elevation transparently. Memory: an editor-as-courier round-trip, since the LSP holds no DAP connection. The LSP hands out tracemalloc/gc injection scripts; the editor runs them in the PAUSED debuggee via DAP `evaluate` (recovering the printed `__BASILISK_MEM*__` payload from `output` events, not the evaluate result), then posts the raw output to the new marker-dispatched `basilisk.memory.ingest`. A new MemorySessionManager scores leaks across diffs (LOW->MEDIUM->HIGH) and publishes purple memory diagnostics. Wires up the previously-unreachable memory diff/gcCollect commands and gates memory commands on an active session. New modules: profiler/memory/session.rs, dap-evaluate.ts, dap-output.ts, memory-ref-graph.ts, subprocess-mode.ts. Validated end-to-end against real debugpy (PID capture + tracemalloc snapshot e2e). Spec updated; touched files kept <500 LOC; VSIX coverage 86% >= 84%. Implements [LSPPROF] / [PROFILE-MEMORY] / [PROFILE-SAME-PROCESS].
…ionProvider + debug activation events
The `basilisk-debug` debugger is factory-based (no program/runtime in the
manifest), so in a workspace without a launch.json the empty-state "Run and
Debug" offered no Basilisk option and F5 did nothing — it only worked where a
launch.json already existed.
Fix:
- Register `createBasiliskDebugConfigProvider` (Dynamic + default): its
provideDebugConfigurations lists "Python: Current File (Basilisk)" in the
Run-and-Debug picker, and resolveDebugConfiguration (pure
applyDebugConfigDefaults) fills an empty/partial config to launch the active
Python file (program: ${file}).
- Add `onDebug` / `onDebugResolve:basilisk-debug` /
`onDebugDynamicConfigurations:basilisk-debug` activation events so the adapter
registers whenever debugging starts, not only after a Python file is opened.
Pure defaulting logic is unit-tested; full debug-integration e2e still green.
VSIX-SPEC documents the zero-config start path.
… menu Two UX gaps reported while testing: nothing visibly showed a snapshot (results only landed as inline decorations on the allocating lines — which are usually library files, not the open file — plus the raw marker JSON in the Debug Console), and every action had to be typed in the command palette. - Wire the (previously built but unused) memory dashboard webview: a snapshot now opens "Basilisk Memory Dashboard" with summary cards (current/peak/gc) and a click-to-navigate top-allocations table; Compare refreshes it with the leak analysis. - Turn the memory status-bar item into a clickable "Memory" menu (Quick Pick: start / snapshot / compare / references / gc / stop), shown whenever a basilisk-debug session is active — no palette typing needed. - Trim the snapshot script to the top 100 allocation sites so the courier's printed __BASILISK_MEM__ line in the Debug Console stays readable. Full VSIX suite green (328 passing); tsc + clippy + eslint clean.
…'s built-in viewer Profiling results now open in VS Code's native profile viewer (flame chart + bottom-up/left-heavy tables) — the same UI as Node.js profiling (https://code.visualstudio.com/docs/nodejs/profiling) — instead of only the custom webviews. - CPU: new profiler/cpuprofile.rs maps the aggregated py-spy stacks to a V8 Profiler.Profile (nodes + samples + integer-µs timeDeltas from the sample rate). Written on profiler.stop and returned as cpuProfilePath; profiler.ts opens it via vscode.open (flamegraph webview kept as a fallback). - Memory: new profiler/memory/heapprofile.rs maps a tracemalloc snapshot to a V8 HeapProfiler.SamplingHeapProfile (head tree, selfSize per site). Written on snapshot ingest and returned as heapProfilePath; memory-profiler.ts opens it (Basilisk dashboard kept for the leak-analysis Compare view). ProfileSession/StopResult now carry sample_rate so timeDeltas are integer microseconds with no float→int cast (as_conversions is denied). V8 schemas verified against the Chrome DevTools Protocol. New unit tests assert both schemas; the memory LSP e2e asserts a valid .heapprofile is produced. Implements [PROFILE-NATIVE]. Spec updated.
export_cpuprofile is only invoked from the profiler-stop handler (which needs a live py-spy session, so the e2e is #[ignore]'d). Add a coarse test that writes a .cpuprofile to a temp dir and parses it back, so the export path is covered.
…six 84→86 memory-dashboard.ts is a webview HTML builder (createWebviewPanel + buildMemoryDashboardHtml, funcs 6.25%) — the same category already excluded from line coverage (memory-ref-graph, profiler-flamegraph-html, memory-decorations, info-panel). It was the lone webview module missing from the exclude list, which dragged the measured total to 81% once this branch's logic landed. Excluding it puts coverage at 87.57%; ratchet the vsix threshold 84→86 per the ratchet-up policy (measured − ~1% buffer).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
CPU and memory profiling now work end-to-end in VS Code against the same process the debugger is attached to, and both export native V8
.cpuprofile/.heapprofilefiles that open in VS Code's built-in profile viewer — plus the debugger now starts with nolaunch.json.What Was Added?
Profiling the live debug session (same process). The LSP holds no DAP connection, so the editor acts as courier:
vscode-extension/src/debug-adapter.ts— aBasiliskDebugAdapterTrackercaptures debugpy'sprocessevent (systemProcessId→ the debuggee PID) andoutputevents (debugpy deliversprint()here, not inevaluate.result).vscode-extension/src/dap-output.ts— per-session output buffer (1 MB cap) for thoseoutputevents.vscode-extension/src/dap-evaluate.ts—evaluateInDebugSession()runs an injection script via DAPevaluateand reconstructs marker-delimited stdout from the output buffer.vscode-extension/src/store.ts—sessionId → pidsignal; "Profile Debug Session" callsbasilisk.profiler.startwith that concrete PID.Memory profiling round-trip (LSP-driven).
crates/basilisk-lsp/src/profiler/memory/session.rs—MemorySessionManager: marker-dispatches courier'd output (__BASILISK_MEM__/_DIFF__/_GC__/_REFS__/_OBJECTS__), accumulates per-session leak history, evicts FIFO (max 32 sessions).crates/basilisk-lsp/src/server/memory_handlers.rs+basilisk-commonMEMORY_INGESTcommand — thebasilisk.memory.ingestround-trip endpoint; publishes allocation/leak diagnostics.vscode-extension/src/memory-ref-graph.ts/subprocess-mode.ts— extracted modules (keep files < 500 LOC).Native V8 profile export (opened via
vscode.open):crates/basilisk-lsp/src/profiler/cpuprofile.rs—build_cpuprofile/export_cpuprofileemit aProfiler.Profile(.cpuprofile): per-thread root-first stacks merged into one call tree, integer-µstimeDeltas(no float→int cast, satisfyingas_conversions).crates/basilisk-lsp/src/profiler/memory/heapprofile.rs—snapshot_to_heapprofileemits aHeapProfiler.SamplingHeapProfile(.heapprofile): onehead-tree child per tracemalloc site withselfSize.Zero-config debugger start —
applyDebugConfigDefaults+createBasiliskDebugConfigProviderindebug-adapter.ts, registered inextension.ts, withonDebug/onDebugResolve/onDebugDynamicConfigurationsactivation events so F5 works without alaunch.json.What Was Changed or Deleted?
vscode-extension/src/memory-profiler.tsreworked (611 LOC churn): courier orchestration, opens.heapprofilenatively (dashboard fallback), status-bar "Memory" button + Quick Pick menu instead of palette-only commands; ref-graph webview moved out tomemory-ref-graph.ts.vscode-extension/src/profiler.ts/profiler-decorations.ts— attach uses the captured PID; opens the.cpuprofile(flamegraph webview fallback);ProfileResult.cpuProfilePath.vscode-extension/src/extension.ts— wires the tracker PID callback into the store, registers the debug-config provider, sets thebasilisk.debuggingcontext (memory commands gated on it).crates/basilisk-lsp/src/profiler/{mod.rs,server/profiler_handlers.rs}—sample_ratethreaded throughProfileSession/StopResult; stop handler returnscpuProfilePath.crates/basilisk-lsp/src/profiler/memory/diagnostics.rs—generate_diff_diagnosticsreturns(leaks, diagnostics)in one scoring pass.vscode-extension/.vscode-test.mjs+coverage-thresholds.json— added thememory-dashboard.jswebview to the coverage-exclude list (the lone webview module missing from it, alongsidememory-ref-graph/profiler-flamegraph-html); VSIX coverage is 87.57% and the vsix threshold is ratcheted 84→86.How Do The Automated Tests Prove It Works?
crates/basilisk-lsp/tests/lsp/ws_test_memory.rs:test_ws_memory_start_then_snapshotdrives the realbasilisk.memory.{start,snapshot,ingest}round-trip over the LSP and asserts the returnedheapProfilePathis a file whosehead.children[0].selfSize == 24_567_890;test_ws_memory_diff_escalates_leak_confidenceasserts LOW→MEDIUM→HIGH across repeated diffs;test_ws_memory_gc_and_referencesasserts gc/refs marker dispatch;test_ws_memory_ingest_unknown_session_errorsasserts an unknown session is rejected (not panicked).tests/memory_session_manager.rs:snapshot_then_repeated_diffs_escalate_leak_confidence,ingest_unknown_session_is_error,ingest_without_marker_is_error.cpuprofile.rs:cpuprofile_matches_v8_schema(node tree + samples + 10 mstimeDeltasat 100 Hz) andexport_writes_a_valid_cpuprofile_file(writes + re-parses the file).heapprofile.rs:heapprofile_matches_v8_schema,empty_snapshot_yields_empty_children.src/test/suite/debug-integration.test.ts: two real-debugpy tests prove PID capture from theprocessevent and a tracemalloc round-trip viaevaluateInDebugSession, plus four pure config-provider tests for zero-config defaults.Spec / Doc Changes
docs/specs/LSP-PROFILING-SPEC.md— added[PROFILE-SAME-PROCESS],[PROFILE-MEMORY-INGEST], and the native-export section; corrected the shared-code table.docs/specs/VSIX-SPEC.md— added[VSIX-PYTHON-DEBUGGER-START]and[VSIX-PYTHON-DEBUGGER-DAP-TRACKER].Breaking Changes