Pular para o conteúdo

Depurar um teste com falha com um agente

Este guia mostra como fazer um agente usar um depurador em uma falha que as instruções de print não explicam.

A ferramenta debug conduz uma sessão real do Debug Adapter Protocol. Um frame parado responde a todas as perguntas sobre aquele momento de uma vez — cada variável local, cada chamador e uma expressão avaliada no próprio escopo do frame —, enquanto uma instrução de print responde apenas à pergunta que você pensou em fazer antes de rodar.

A ferramenta permanece na superfície para que o agente possa perguntar por que nada está utilizável, em vez de adivinhar. Um adaptador só consegue de fato iniciar quando as duas condições a seguir valem:

  • um marcador de projeto no checkout, e
  • o binário do adaptador sendo resolvido.

Peça ao agente para verificar:

debug(op: "adapters")

Ele responde com o que encontrou ou explica que um adaptador precisa das duas metades.

Linguagem Adaptador Marcador Instalação
Dart / Flutter dart debug_adapter pubspec.yaml Vem com o SDK — nada a fazer
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Vem com o LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

Defina o ponto de interrupção primeiro, e só então inicie

Seção intitulada “Defina o ponto de interrupção primeiro, e só então inicie”

A ordem importa, e a ferramenta a impõe: pontos de interrupção podem ser definidos antes de existir uma sessão, porque é essa a sequência que o protocolo exige.

Peça ao agente algo como:

A função compute em bin/main.dart retorna o valor errado. Defina um ponto de interrupção na linha que atribui product, inicie o programa sob o depurador e me diga quais são de fato os valores de a e b.

Ele vai executar mais ou menos isto:

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

A inicialização informa onde o programa parou:

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)

Depois, entre nele:

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

Um objeto com filhos informa [expand: reference N] — mas só quando há de fato algo por trás dele, de modo que uma expansão que o agente não pode usar nunca é oferecida.

Para qualquer coisa que não apareça como variável local:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
O que você quer Operação
Rodar até a próxima parada continue
Próxima linha, mesmo frame step_over
Entrar na chamada step_in
Sair para o chamador step_out
Parar um programa em execução pause

Para um processo travado, e não um que você está iniciando:

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

Sempre encerre a sessão. Um adaptador de depuração é dono de um processo parado que segura o que quer que aquele processo segure — uma porta, um lock, uma conexão de banco de dados.

Uma expansão de variables em um grafo de objetos profundo é ilimitada por natureza — um único widget do Flutter alcança a árvore de elementos inteira. Filhos por escopo são limitados a 50, valores a 400 caracteres e frames de pilha a 30. Um resultado de ferramenta que enche a janela de contexto é uma ferramenta que encerra a execução que estava ajudando.