用代理除錯失敗的測試
本指南說明如何讓代理針對列印陳述式解釋不了的失敗,改用除錯器來處理。
debug 工具會驅動真正的 Debug Adapter Protocol 工作階段。停止的框架能一次回答關於那個時刻的每個問題——每個區域變數、每個呼叫者,以及在該框架自身範圍內求值的運算式——而列印陳述式只回答你在執行前就想到要問的那個問題。
開始之前
標題為「開始之前」的章節這項工具會把狀態攤在檯面上,讓代理可以追問為什麼什麼都不能用,而不是用猜的。只有同時符合以下兩者,配接器才能真正啟動:
- 簽出的內容中有專案標記,以及
- 配接器的執行檔能解析得到。
請代理檢查:
debug(op: "adapters")它會回報找到什麼,或說明配接器需要這兩個條件同時成立。
| 語言 | 配接器 | 標記 | 安裝 |
|---|---|---|---|
| Dart / Flutter | dart debug_adapter |
pubspec.yaml |
隨 SDK 提供——不必做任何事 |
| Python | python3 -m debugpy.adapter |
pyproject.toml, setup.py, requirements.txt |
pip install debugpy |
| Rust / C / C++ | lldb-dap |
Cargo.toml, CMakeLists.txt |
隨 LLVM 提供 |
| Go | dlv dap |
go.mod |
go install github.com/go-delve/delve/cmd/dlv@latest |
先設定中斷點,再啟動
標題為「先設定中斷點,再啟動」的章節順序很重要,而工具會強制要求這個順序:中斷點可以在工作階段存在之前就設定,因為那正是協定要求的順序。
請代理做類似這樣的事:
bin/main.dart裡的compute函式回傳了錯誤的值。在指定product的那一行設定中斷點,在除錯器中啟動它,然後告訴我a和b實際上是多少。
它大致上會執行:
debug(op: "breakpoints", file: "bin/main.dart", lines: [7])debug(op: "launch", program: "bin/main.dart")啟動後會回報它停在哪裡:
Launched bin/main.dart on dart (session dart-1). Stopped: breakpoint. Use `stack` to see where.讀取框架
標題為「讀取框架」的章節debug(op: "stack")→ #1 compute (bin/main.dart:7:3) #2 main (bin/main.dart:2:18)接著走進去:
debug(op: "scopes", frame_id: 1)→ Locals (reference 12)
debug(op: "variables", reference: 12)→ a (int) = 6 b (int) = 7 product (int) = 42帶有子項的物件會回報 [expand: reference N]——但只有在背後確實有東西時才會如此,所以代理用不到的展開項目永遠不會被提供。
對於任何不是以區域變數呈現的內容:
debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)逐步執行
標題為「逐步執行」的章節| 你想要什麼 | 操作 |
|---|---|
| 執行到下一個停止點 | continue |
| 下一行,同一個框架 | step_over |
| 進入呼叫 | step_in |
| 返回呼叫者 | step_out |
| 停止正在執行的程式 | pause |
附加到已在執行的行程
標題為「附加到已在執行的行程」的章節用於卡死的行程,而不是你正在啟動的行程:
debug(op: "attach", port: 5858)debug(op: "terminate")一定要結束工作階段。除錯配接器擁有一個停止的行程,並持有那個行程所持有的一切——連接埠、鎖定、資料庫連線。
有界輸出
標題為「有界輸出」的章節對深層物件圖展開 variables 本質上是沒有界的——一個 Flutter widget 就能觸及整棵元素樹。每個範圍的子項上限為 50,值上限為 400 個字元,堆疊框架上限為 30。一個塞爆情境視窗的工具結果,就是終結它原本在協助的那次執行的工具。