Salta ai contenuti

Esegui il debug di un test fallito con un agente

Questa guida mostra come far usare a un agente un debugger su un fallimento che le istruzioni print non spiegano.

Lo strumento debug pilota una sessione reale del Debug Adapter Protocol. Un frame in pausa risponde in una volta sola a tutte le domande su quel momento — ogni variabile locale, ogni chiamante e un’espressione valutata nello scope del frame stesso — mentre un’istruzione print risponde solo alla domanda che hai pensato di fare prima di avviare il programma.

Lo strumento resta esposto così l’agente può chiedere perché nulla è utilizzabile, invece di tirare a indovinare. Un adattatore può davvero partire solo quando valgono entrambe queste condizioni:

  • un marcatore di progetto nel checkout, e
  • il binario dell’adattatore che si risolve.

Chiedi all’agente di verificare:

debug(op: "adapters")

Risponde con ciò che ha trovato, oppure spiega che un adattatore ha bisogno di entrambe le metà.

Linguaggio Adattatore Marcatore Installazione
Dart / Flutter dart debug_adapter pubspec.yaml Fornito con l’SDK — non devi fare nulla
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Fornito con LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

L’ordine conta e lo strumento lo impone: i breakpoint si possono impostare prima che esista una sessione, perché è la sequenza richiesta dal protocollo.

Chiedi all’agente qualcosa del genere:

La funzione compute in bin/main.dart restituisce il valore sbagliato. Imposta un breakpoint sulla riga che assegna product, avviala sotto il debugger e dimmi cosa valgono davvero a e b.

Eseguirà all’incirca:

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

L’avvio segnala dove si è fermato:

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)

Poi entraci dentro:

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

Un oggetto con figli segnala [expand: reference N] — ma solo quando c’è davvero qualcosa dietro, così un’espansione che l’agente non può usare non viene mai proposta.

Per qualsiasi cosa non visibile come variabile locale:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
Cosa vuoi fare Op
Esegui fino alla prossima sosta continue
Riga successiva, stesso frame step_over
Entra nella chiamata step_in
Esci verso il chiamante step_out
Ferma un programma in esecuzione pause

Per un processo bloccato invece che per uno che stai avviando tu:

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

Chiudi sempre la sessione. Un adattatore di debug possiede un processo in pausa che trattiene tutto ciò che quel processo trattiene: una porta, un lock, una connessione a un database.

Un’espansione variables su un grafo di oggetti profondo è illimitata per natura: un solo widget Flutter raggiunge l’intero albero degli elementi. I figli per scope sono limitati a 50, i valori a 400 caratteri e gli stack frame a 30. Un risultato di strumento che riempie la finestra di contesto è uno strumento che mette fine all’esecuzione che stava aiutando.