Przejdź do głównej zawartości

Debuguj padający test z agentem

W tym poradniku pokazujemy, jak skłonić agenta do użycia debuggera przy awarii, której nie wyjaśniają instrukcje print.

Narzędzie debug prowadzi prawdziwą sesję Debug Adapter Protocol. Zatrzymana ramka odpowiada naraz na każde pytanie o ten moment — o każdą zmienną lokalną, każdego wywołującego i o wyrażenie obliczone w zasięgu samej ramki — podczas gdy instrukcja print odpowiada tylko na pytanie, które przyszło Ci do głowy przed uruchomieniem.

Narzędzie pozostaje na powierzchni, żeby agent mógł zapytać, dlaczego nic nie da się użyć, zamiast zgadywać. Adapter może faktycznie wystartować tylko wtedy, gdy zachodzą oba poniższe warunki:

  • znacznik projektu w checkoutcie oraz
  • rozwiązanie ścieżki pliku binarnego adaptera.

Poproś agenta o sprawdzenie:

debug(op: "adapters")

Odpowiada tym, co znalazł, albo wyjaśnia, że adapter potrzebuje obu połówek.

Język Adapter Znacznik Instalacja
Dart / Flutter dart debug_adapter pubspec.yaml Dostarczany z SDK — nic nie trzeba robić
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Dostarczany z LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

Kolejność ma znaczenie i narzędzie jej pilnuje: punkty przerwania można ustawiać przed istnieniem sesji, bo takiej sekwencji wymaga protokół.

Poproś agenta o coś w tym stylu:

Funkcja compute w bin/main.dart zwraca nieprawidłową wartość. Ustaw punkt przerwania w wierszu, który przypisuje product, uruchom to pod debuggerem i powiedz mi, jakie faktycznie są a i b.

Wykona w przybliżeniu:

debug(op: "breakpoints", file: "bin/main.dart", lines: [7])
debug(op: "launch", program: "bin/main.dart")

Uruchomienie zgłasza, gdzie się zatrzymało:

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)

Następnie wejdź w głąb:

debug(op: "scopes", frame_id: 1)
→ Locals (reference 12)
debug(op: "variables", reference: 12)
→ a (int) = 6
b (int) = 7
product (int) = 42

Obiekt z dziećmi zgłasza [expand: reference N] — ale tylko wtedy, gdy naprawdę coś za nim stoi, więc rozwinięcie, którego agent nie może użyć, nigdy nie jest proponowane.

Dla wszystkiego, co nie jest widoczne jako zmienna lokalna:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
Co chcesz zrobić Operacja
Bieg do następnego zatrzymania continue
Następny wiersz, ta sama ramka step_over
Wejście do wywołania step_in
Wyjście do wywołującego step_out
Zatrzymanie działającego programu pause

Dla zawieszonego procesu, a nie takiego, który dopiero uruchamiasz:

debug(op: "attach", port: 5858)
debug(op: "terminate")

Zawsze kończ sesję. Adapter debuggera posiada zatrzymany proces, który trzyma wszystko, co trzyma ten proces — port, blokadę, połączenie z bazą danych.

Rozwinięcie variables na głębokim grafie obiektów jest z natury nieograniczone — jeden widget Fluttera sięga całego drzewa elementów. Liczba dzieci na zasięg jest ograniczona do 50, wartości do 400 znaków, a ramek stosu do 30. Wynik narzędzia, który zapełnia okno kontekstu, to narzędzie, które kończy uruchomienie, któremu pomagało.