Skip to content

feat(windows): 复用 Rust Debug Core 并实现原生 DAP Host Adapter (#374) - #381

Open
puppyben1 wants to merge 8 commits into
1lck:previewfrom
puppyben1:codex/issue-374-windows-dap
Open

feat(windows): 复用 Rust Debug Core 并实现原生 DAP Host Adapter (#374)#381
puppyben1 wants to merge 8 commits into
1lck:previewfrom
puppyben1:codex/issue-374-windows-dap

Conversation

@puppyben1

@puppyben1 puppyben1 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

背景

Windows Debug 前端已存在,但原生 backend 未接通。本 PR 实现薄层 Windows Debug Host Adapter:

  • 新增 Tauri host debug.rs:adapter 进程/stdio 传输/会话生命周期(多 session 隔离、幂等 Stop、退出清理、pid 校验防 stale 事件)
  • 所有 DAP 语义走共享 lithe-core 的 debug.* 契约(createSession/launch/setBreakpoints/inspect/execute/disconnect/destroySession)
  • debug_send_request 仅作临时兼容 facade,内部委托 Core
  • 前端改为消费 Core 归一化事件(stateChanged/stopped/continued/terminated/output/operationCompleted/operationFailed),删除 React 侧 DAP 状态机
  • 按验收标准打开 �ackendCapabilities.debugger`n

验证

  • 前端全量 �un test src:507 pass(含 debugger 事件桥 6 项)
  • cargo test(src-tauri):110 pass(含 debug.rs 10 项)
  • sc --noEmit、�erify-windows-boundaries.ps1 通过
  • scripts/build-windows.ps1 -Configuration Release 构建成功

@puppyben1
puppyben1 requested a review from 1lck as a code owner September 1, 2026 07:36
Comment thread windows/tauri/src-tauri/src/debug.rs Outdated
Comment thread windows/tauri/src/features/debugger/services/debug-adapter-events.ts Outdated
Comment thread windows/tauri/src-tauri/src/debug.rs Outdated
Comment thread windows/tauri/src-tauri/src/main.rs
Comment thread windows/tauri/src/features/debugger/services/debug-adapter-events.test.ts Outdated

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@puppyben1 这次实现的整体方向是对的:DAP framing、请求匹配和 session 状态机继续由共享 lithe-core 负责,Windows host 只承接进程、stdio 和平台事件;PID/session generation 校验及幂等 Stop 的设计也值得保留。

目前仍有阻塞合并的问题,主要集中在真实集成路径和生命周期上:attach command 与 facade 不匹配且失败后会遗留进程;旧 session 的延迟事件能够修改快速重启后的新 session;Core 的 failed 状态以及 stdout EOF/read error 没有触发完整 teardown;项目关闭时也没有停止所属 adapter。以上路径都直接属于 issue #374 的验收范围。

测试方面,新增事件桥测试使用真实 timer,当前稳定性门禁已经失败,因此 Windows 构建、Frontend 测试和 Windows Rust timing harness 实际都没有在本次 PR CI 中执行。建议修复上述生命周期问题后,用带有界 watchdog 的 fake adapter 补齐 attach、initialize/launch rejection、分片/连续 frame、stderr、EOF、write failure、多 session、重复 Stop 和快速 Stop/Restart,再重新跑完整 Windows 验证。

@puppyben1

Copy link
Copy Markdown
Contributor Author

已按 review 意见修复并推送 commit c9392fe

  • facade 接受 attach,且所有失败路径回收 session/process
  • 事件桥丢弃非 active session 的状态/结果事件(保留日志输出)
  • failed 状态作为终态处理(前端 + native 回收)
  • stdout EOF/read error 走 exactly-once fail 路径(含 sessionId/PID/阶段)
  • 新增 debug_stop_workspace_sessions,项目关闭/切换时只停对应 workspace 的 adapter
  • 测试去掉真实 timer,事件桥返回可等待 Promise

验证:

  • cargo test(src-tauri):113 pass(新增 attach/workspace 匹配/failed update 3 项)
  • �un test src:509 pass(新增 stale event、failed 状态 2 项)
  • �erify-test-stability.ps1 通过; est-stability-windows.ps1 -Scope Frontend 全部 509 项计入报告
  • sc --noEmit、�erify-windows-boundaries.ps1 通过
  • scripts/build-windows.ps1 -Configuration Release 构建成功

Comment thread windows/tauri/src-tauri/src/debug.rs
Comment thread windows/tauri/src-tauri/src/debug.rs
Comment thread windows/tauri/src/features/debugger/services/debug-adapter-events.ts Outdated
Comment thread windows/tauri/src-tauri/src/debug.rs Outdated
Comment thread windows/tauri/src-tauri/src/debug.rs

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@puppyben1 我重新按 issue #374 和最新 head c9392fe2 走了一遍完整链路。上次的 attach facade、跨 session stale event、workspace 归属入口以及 timer-free 测试都已经按方向修正,这些改动是有效的;继续把 DAP framing、请求匹配和状态机留在共享 lithe-core,Windows 只负责进程与 stdio,这个架构边界也值得保留。

目前仍有阻塞合并的问题:正常 adapter 退出会被 EOF 分支抢先标成 failed;adapter 拒绝 initialize/launch 后,stdout receive 路径没有进入 native exactly-once teardown;operation context 在 frame 发出后才登记,快速 adapter 会稳定丢失 variables/evaluate 结果;workspace 批量停止会因单个失败提前退出并留下其他 session。另有缺省 cwd 未回落到 workspace 的兼容性问题。

验证方面,Windows CI 已通过,Windows Rust timing harness 记录了 113 个测试;本地 verify-test-stabilityverify-windows-boundaries、新增 debugger 前端 8 项测试和 git diff --check 通过。但现有测试仍未覆盖 issue 要求的真实 fake-adapter/process/stdio 生命周期,Windows CI 也没有执行新增的 debugger 前端测试。另有两个非核心但应顺手处理的验证缺口:cargo fmt --check 当前失败;在 macOS 运行 Windows host 的 focused Rust tests 时,Windows 路径断言导致 12/13 通过。

因此这轮仍建议 Request Changes。把上述生命周期和关联时序收紧,并补上真正经过 stdio/process 的确定性测试后,这个实现的整体方向可以继续保留。

@puppyben1

Copy link
Copy Markdown
Contributor Author

感谢 review。已推送 commit e0dbe42,逐项处理如下:

  1. 正常 adapter 退出:stdout EOF 不再直接标记 failed;由 exit waiter 统一处理退出状态并发布 exited/exitCode。若 stdout EOF 但进程仍存活,reader 会终止该进程,避免 session 泄漏。
  2. initialize/launch 拒绝:stdout receive 路径检查 Core update.state == failed,并通过 native fail_session 执行 exactly-once teardown。
  3. operation context:evaluate、variables、scopes、threads、stackTrace 现在先生成并登记 operationId,再发送请求,避免快速 adapter 丢失结果。
  4. workspace 批量停止:改为 best-effort,单个 session 失败不会阻断其他 session。
  5. 默认 cwd:使用 effectiveCwd = config.cwd ?? workspacePath,并用于 adapter、Core rootPath 和 launch payload。
  6. 测试与 CI:事件桥继续使用可 await Promise;debug-adapter-events.test.ts 已加入 Windows frontend CI。

当前环境无法运行 cargo/bun,因此真实 fake-adapter/process/stdio 生命周期测试及 Windows timing harness 仍需在 Windows 环境手动确认。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@puppyben1 我又按 issue #374 和当前 head 把链路过了一遍。上一轮你修的 attach facade、stale restart 过滤、failed 终态、EOF 不再误标 failed、operationId 先登记、workspace best-effort stop、effectiveCwd,方向都对,这些我这边也关掉了。

整体判断:架构边界仍然值得保留——DAP framing / 请求匹配 / 状态机继续放在共享 lithe-core,Windows host 只做进程、stdio 和生命周期,这个分层不要动。

目前还有 2 个会阻塞合并的问题

  1. Start 时 activeSession 登记过晚,和事件桥的 stale filter 叠在一起,快速 adapter 下会丢掉 stopped 等关键事件。
  2. 仍然缺少 issue 要求的 fake adapter / 真实 process·stdio 生命周期测试;capability 已经打开,这层不能只靠直接调 Core 的单测顶着。

另外标了 2 条 P2(receive failed 先 emit、stop 写失败也发 ended),不阻塞合并,但改动很小,建议顺手收掉。

验证上:本地我没有完整重跑 Windows cargo test / Release 构建;合并前请确认 Windows CI、新增 process 测,以及 debugger 前端测都绿。补上 fake adapter 之后,把「正常退出 → EOF → exited」和「启动中途 stopped 不被丢」这两条锁住会安心很多。

breakpoints,
rootFolderPath,
);
debuggerActions.startSession({

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] windows/tauri/src/features/debugger/components/debugger-view.tsx:232

startSession 要等 startDebugLaunchSession() 整段返回才写入 store,而那一步内部已经完成了 debug_start_session、launch/attach 和 setBreakpoints。事件桥这边又会丢掉 sessionId !== activeSession?.id 的非 output 事件(debug-adapter-events.ts 约 49–51 行)。

触发场景:adapter 较快(或 stopOnEntry)时,stopped / stateChanged / operationCompleted 会在 await 间隙到达;此时 activeSession 仍是 null,事件被静默丢弃。结果是 native session 已在跑,UI 可能一直停在 running,栈和线程出不来。

建议:debug_start_session 一成功就先 startSession({ id, status: "starting" | "running" }),再发 launch/breakpoints;或者在事件桥维护 acceptedSessionIds(start 时加入,ended/stop 时移除)。最好补一条测试:start 过程中注入 stopped,断言 store 进入 paused 并 cascade stackTrace。

}

#[test]
fn facade_launch_reaches_the_adapter_through_the_shared_state_machine() {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] windows/tauri/src-tauri/src/debug.rs:1090

这个用例直接调 Core 并手工注入 DAP bytes,没有真正起 child,也没有走 stdin/stdout/stderr/exit waiter。issue #374 要求的分片/连续 frame、stderr、EOF、写失败、异常退出、多 session、重复 Stop、快速 Start/Stop,以及 reader cleanup,目前都还锁不住。

上一轮「正常退出被 EOF 标成 failed」就是这类缺口里长出来的;现在逻辑改过了,但没有 process 级回归,后面很容易再漂。backendCapabilities.debugger 已经打开,这层测试更应该补上。

建议加一个不依赖本机 Java/Node 等工具的 fake adapter(小 exe 或测试内嵌进程),带有界等待和明确 cleanup,至少覆盖:正常 exited、stdout 先关进程仍存活、write 失败、双 session 不串线、Stop 两次幂等。前端事件桥测可以继续保留,但不能替代 host 这一层。

if !is_current_session(&session_id, pid) {
break;
}
if update_state_failed(&update) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] windows/tauri/src-tauri/src/debug.rs:705(不阻塞合并)

receive 路径在 state == failed 时直接 fail_session,没有先 emit_update_eventsdebug_send_request 那边是先 emit 再 fail,两边不一致。

影响:Core 带出来的 operationFailed / stateChanged(failed) 到不了前端,只能靠 stderr 文本 + session_ended;如果当时已经有 pending request,也不一定能走 operationFailed 清掉。

建议和 send 路径对齐:先 emit_update_events,再 fail_session。native 继续做唯一的资源 owner 没问题。

.await
{
Ok(update) => {
if let Err(error) = write_outbound_frames(&stdin, &update) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] windows/tauri/src-tauri/src/debug.rs:342(不阻塞合并)

disconnect 的 outbound frame 写失败时,这里会 kill + destroySession 然后 return Err,但不会发 debugger_session_ended。session 已经从 registry 摘掉了,进程也杀了。

从 DebuggerView 点 Stop 时 UI 往往已经自己 stopSession(),不太明显;但 debug_stop_workspace_sessions 这类只依赖 ended 事件的路径,store 可能还会以为 session 还在。

建议这条失败分支也走同一套终态(emit ended,reason 可以是 failedstopped),继续用 pid/session 校验保证 exactly-once。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants