用代理除錯失敗的測試
本指南說明如何讓代理在打印語句解釋不了的失敗上使用除錯器。
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。一個填滿上下文窗口的工具結果,就是一個會終結它原本在協助的那次執行的工具。