Recording replay fixtures from VS Code¶
The replay tests under tests/replay are driven by recorded DAP sessions. A fixture must come
from a real VS Code session: only a real client capture proves the request ordering an actual
client produces, which is exactly what the strict-order replay matcher asserts. This page lists
the manual debugging sessions to perform and the exact gestures that trigger each DAP request.
The six feature scenarios below do not have fixtures yet; their TEST(Replay, ...) entries in
tests/replay/replay_tests.cpp skip (with the missing fixture named in the skip message) until
the scenario has been recorded and normalized.
How a session gets recorded¶
Every configuration in .vscode/launch.json sets a trace path, so each debug session is
automatically written to recordings/launch.session.json (gitignored). Each new session
overwrites that file: copy it to a scenario-specific name immediately after every session, for
example:
Prerequisites:
- A built Debug tree:
npm run configureandnpm run build(adapter plus thetest-targetsdebuggees). - Set
"dbgengPath"in the launch configuration to thedbgeng.dllyou debug with.Normalize-DapRecording.ps1rewrites that value to the${dbgEngPath}token, which the replay harness resolves throughDAP_DBGENG_WINDBG_PATH(or the default SDK install). A capture recorded without it only replays on machines with the SDK Debugging Tools at the default location. - The "Debug Test Target (Launch)" configuration omits
program, so select the debuggee per scenario via the CMake Tools active launch target in the status bar (test_struct_1,test_launch,test_exception_1,test_data_1). - The memory scenario needs the Hex Editor extension (
ms-vscode.hexeditor).
The deliverable per scenario is the raw, renamed capture under recordings/. Normalization
(scripts/Normalize-DapRecording.ps1), creating the fixture under tests/replay/data/, and
adjusting the matching TEST(Replay, ...) assertions happen afterwards.
Ground rules for clean captures¶
- One scenario per session, starting from a clean slate: before launching, remove all leftover breakpoints (source, function, instruction, and data), remove watch expressions, untick exception filters, and close the hex editor and disassembly view from a previous scenario. Anything left over injects extra requests into the capture.
- Avoid hovering over source or variables while recording (every hover is an extra
evaluaterequest), and expand only the variables the scenario calls for. Extra traffic does not break the replay, but it bloats the fixture and adds nondeterminism. - End every scenario by continuing until the program exits, and let the session end on its own.
Scenarios¶
1. Memory read and write - creates memory-read-write.json¶
Target: test_struct_1 (the test asserts p at {3, 7}, then 42 written back).
- Set a source breakpoint on
struct-1.cppline 37 (thestd::coutline), then F5. - In the Variables view, select
pand use View Binary Data (the inline button on the variable, provided by the Hex Editor extension). This sendsreadMemory. - Switch the hex editor to Replace mode first: click the Insert indicator in the status
bar (bottom right) so it reads Replace. The hex editor defaults to Insert mode, and an
insert edit cannot be saved to debug memory (memory cannot be resized) - Save then fails
client-side with "Not supported" and no
writeMemoryis ever sent. - Change the first byte from
03to2A(42 little-endian) and save. This sendswriteMemoryfollowed by a re-read. - Close the hex editor, then continue to exit.
Note: the modules request cannot be recorded from VS Code (see
what cannot be recorded); it has no replay fixture and is
covered by the live integration and handler tests instead.
2. Set expression - creates set-expression.json¶
Target: test_struct_1 (fixture assigns t.origin.x = 123 and observes it).
- Set a source breakpoint on
struct-1.cppline 37, then F5. - In the Watch view, add the expression
t.origin.x. - Right-click the watch entry and choose Set Value, enter
123. Editing a watch value sendssetExpression; editing in the Variables view would sendsetVariableinstead, so the Watch view is mandatory here. - In the Variables view, expand
t, thenorigin, and confirmxreads123. - Remove the watch expression, then continue to exit.
3. Function breakpoints - creates function-breakpoints.json¶
Target: test_launch (fixture stops in main via a deferred function breakpoint).
- With no source breakpoints set, use the + button in the Breakpoints view header (Add
Function Breakpoint) and enter
test_launch!main. - F5: the session stops in
main. - Continue to exit, then remove the function breakpoint.
4. Instruction breakpoints - creates instruction-breakpoints.json¶
Target: test_struct_1 (fixture stops at a disassembly address past the current one).
- Set a source breakpoint on
struct-1.cppline 37, then F5. - When stopped, right-click in the editor and choose Open Disassembly View (this sends
disassemble). - In the disassembly view, click the gutter of an instruction a few lines below the current one.
This sends
setInstructionBreakpoints. - Continue: the session stops at that instruction.
- Remove the instruction breakpoint, then continue to exit.
5. Exception filter - creates exception-filter.json¶
Target: test_exception_1 (fixture stops first-chance on a caught std::runtime_error).
- With no breakpoints set, tick the C++ exceptions checkbox in the Breakpoints view.
- F5: the session stops on the
throwwith the exception widget open (the widget triggersexceptionInfo; expect exception code0xE06D7363). - Continue: the exception is caught locally, so the program runs to a clean exit.
- Untick the C++ exceptions filter.
6. Data breakpoints - creates data-breakpoints.json¶
Target: test_data_1 (fixture arms a write watchpoint on watched and observes the write).
- Set a source breakpoint on
data-1.cppline 15 (the "data-1 armed" line), then F5. - In the Variables view, right-click
watchedand choose Break on Value Change. This sendsdataBreakpointInfofollowed bysetDataBreakpoints. - Continue: the watchpoint fires on the
watched = next * 2write (line 17);watchednow reads4. - Remove the data breakpoint in the Breakpoints view before continuing. The hardware
watchpoint sits on a stack address that is reused after
mainreturns, so a leftover watchpoint re-fires forever and the program never exits. - Continue to exit.
What cannot be recorded from VS Code¶
modules- VS Code has no Modules view (it is an open feature request, microsoft/vscode#110067) and never sends themodulesrequest, so no UI gesture can produce it. The request has no replay fixture; the live integration test (LaunchGetModulesListsTheDebuggee) and the handler tests cover it.
Turning a capture into a fixture¶
For each delivered recordings/<scenario>.session.json:
- Normalize it into its fixture:
pwsh scripts/Normalize-DapRecording.ps1 recordings/set-expression.session.json `
tests/replay/data/set-expression.json
- Review the matching
TEST(Replay, ...)intests/replay/replay_tests.cpp: several tests assert values from the fixture (for examplewatchedreading4, or exception code0xE06D7363) and their expectations may need adjusting to the recorded session's shape. - Run
npm test(theReplaysuite replays the fixture againstdap-dbgeng.exe). - Run
npm run matrixand commitdocs/development/request-coverage.mdif it changed (replay fixtures feed the coverage page).