跳至內容

用代理除錯失敗的測試

本指南說明如何讓代理在打印語句解釋不了的失敗上使用除錯器。

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。一個填滿上下文窗口的工具結果,就是一個會終結它原本在協助的那次執行的工具。