feat(windows): 复用 Rust Debug Core 并实现原生 DAP Host Adapter (#374) - #381
feat(windows): 复用 Rust Debug Core 并实现原生 DAP Host Adapter (#374)#381puppyben1 wants to merge 8 commits into
Conversation
1lck
left a comment
There was a problem hiding this comment.
@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 验证。
…iled state, stale events, timer-free tests)
|
已按 review 意见修复并推送 commit c9392fe:
验证:
|
1lck
left a comment
There was a problem hiding this comment.
@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-stability、verify-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 的确定性测试后,这个实现的整体方向可以继续保留。
|
感谢 review。已推送 commit e0dbe42,逐项处理如下:
当前环境无法运行 cargo/bun,因此真实 fake-adapter/process/stdio 生命周期测试及 Windows timing harness 仍需在 Windows 环境手动确认。 |
1lck
left a comment
There was a problem hiding this comment.
@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 个会阻塞合并的问题:
- Start 时
activeSession登记过晚,和事件桥的 stale filter 叠在一起,快速 adapter 下会丢掉stopped等关键事件。 - 仍然缺少 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({ |
There was a problem hiding this comment.
[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() { |
There was a problem hiding this comment.
[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) { |
There was a problem hiding this comment.
[P2] windows/tauri/src-tauri/src/debug.rs:705(不阻塞合并)
receive 路径在 state == failed 时直接 fail_session,没有先 emit_update_events。debug_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) { |
There was a problem hiding this comment.
[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 可以是 failed 或 stopped),继续用 pid/session 校验保证 exactly-once。
背景
Windows Debug 前端已存在,但原生 backend 未接通。本 PR 实现薄层 Windows Debug Host Adapter:
验证