Dev Life

Apresentamos um depurador multiplataforma para Go

Usamos Go para grande parte do nosso desenvolvimento de servidores aqui no Mailgun, e é ótimo. No entanto, vindo do Python, houve uma coisa da qual senti muita falta:
Imagem para Apresentamos um depurador multiplataforma para Go

Usamos Go para grande parte do nosso desenvolvimento de servidores aqui no Mailgun, e é ótimo. No entanto, vindo do Python, houve uma coisa da qual senti muita falta:

Eu podia inserir esta pequena linha sempre que ficasse confuso sobre o que estava acontecendo no código e ver exatamente o que se passava. Mas e no Go? Nem tanto. Quando comecei este projeto em janeiro, o gdb falhava em todos os programas nos quais eu testava. O delve não funcionava no OS X, e o uso de prints na depuração era muito lento e limitado. O que uma pessoa desenvolvedora deve fazer?

Criar meu próprio depurador, é claro.

godebug

Tudo o que está no caminho [de um bom depurador de Go] é a escrita de muito código de baixo nível e não portátil, comunicando-se com interfaces problemáticas e não documentadas.
Rob Pike

godebug é um tipo diferente de depurador. Depuradores tradicionais para linguagens compiladas usam chamadas de sistema de baixo nível e leem arquivos binários para depurar símbolos. Eles são difíceis de acertar e difíceis de portar.

O godebug adota uma abordagem diferente: pega o código-fonte de um programa de destino, insere o código de depuração entre todas as linhas, em seguida compila e executa o resultado. O resultado é um depurador totalmente funcional e extremamente portátil. Na verdade, graças ao gopherjs, você pode executá-lo bem aqui no seu navegador!

Você pode editar o programa e reiniciá-lo com o botão “DEBUG IT!” quantas vezes quiser.

Como isso funcionou?

Aqui está um diagrama rápido da demonstração acima:

O código original é transformado duas vezes. Primeiro, o godebug inseriu a instrumentação de depuração. Em seguida, o gopherjs compilou o resultado para javascript.

Vamos dar uma olhada na etapa de instrumentação. (Para saber mais sobre a parte do gopherjs (que é incrível), confira o seu site.) Aqui estão algumas das chamadas que o godebug insere:

  • O godebug.EnterFunc informa à biblioteca de tempo de execução do godebug que estamos entrando em uma função. Como o “next” não para dentro de chamadas de função, a biblioteca de tempo de execução anota essas chamadas para saber quando pular as linhas.
  • O godebug.ExitFunc informa à biblioteca de tempo de execução que estamos saindo de uma função. Omitido em main.
  • O godebug.Line faz com que o programa pause e espere por uma entrada se, e somente se, um comando do usuário ou um breakpoint o ordenar. Quando pausa, ele solicita a entrada do usuário e responde a quaisquer comandos.
  • O godebug.Declare registra o mapeamento dos nomes de variável para seus valores. Esse mapeamento é usado pelo comando print.

Esta é uma visão geral resumida. Existem outras funções que o godebug insere, e muitos detalhes das funções acima foram omitidos. Mas essas são as partes básicas de como o godebug funciona.

Como usar o godebug

Tudo isso (menos a parte do servidor e do javascript) está empacotado na ferramenta de linha de comando godebug. Veja como usá-lo:

Passo 1. Como instalar

$ go get -u github.com/mailgun/godebug

Passo 2. Como definir breakpoints

Adicione este marcador em qualquer lugar que você queira um breakpoint:

_ = "breakpoint"

Essa declaração se torna um breakpoint quando executada sob o godebug e é uma operação nula caso contrário.

Como os breakpoints fazem parte do código-fonte, você pode envolver sua própria lógica ao redor deles. Digamos que você esteja executando um teste orientado a tabelas com dezenas de casos, e um dos casos de teste falha: aquele que testou a entrada "weird string". Você pode adicionar este breakpoint ao seu teste:

for _, tt := range myTestCases { if tt.in == "weird string" { _ = "breakpoint" } ... }

O godebug test pausará o programa na declaração do marcador, que está convenientemente posicionada logo antes da execução do caso de teste que falhou.

Passo 3. Como executar seu programa

Use o comando godebug run:

godebug run

Ou, para testes, use o comando godebug test:

godebug test

Por padrão, o godebug só adicionará a instrumentação de depuração ao pacote main (para o godebug run) ou ao pacote em teste (para o godebug test). Ele não instrumentará nenhum pacote importado. Isso serve para diminuir a sobrecarga do depurador — quaisquer pacotes que não sejam do seu interesse rodarão normalmente, em velocidade total.

Isso significa que, por padrão, você não pode entrar em chamadas de função de pacotes importados. Mas às vezes você vai precisar fazer isso! Para depurar outros pacotes, passe a flag -instrument para o godebug run ou godebug test:

godebug test -instrument=pkgA,pkgB,pkgC pkg/under/test

O comando acima irá instrumentar (e, portanto, permitir que você entre em) pkgApkgBpkgCpkg/under/test. Em seguida, ele criará e executará os testes para pkg/under/test. Uma semântica semelhante se aplica ao godebug run.

Experimente!

Da próxima vez que quiser entender o que está acontecendo em um programa Go, experimente o godebug. Tenha em mente que ainda é uma ferramenta nova que precisa de um certo polimento. Em particular, algumas limitações conhecidas são:

  • sobrecarga de desempenho
  • pode causar conflitos de leitura se o seu programa ler a partir do stdin
  • não pode ser anexado a um processo em execução
  • é preciso conhecer os pacotes que você deseja depurar antes de iniciar a sessão

Dito isso, estou empolgado com esta ferramenta e espero que ela possa agregar muito valor à comunidade Go. Experimente e diga o que você achou! Se você tiver algum problema, ficarei feliz em saber. Abra uma issue no https://github.com/mailgun/godebug/issues ou envie um e-mail para: dev@godebug.rocks

Espero que você goste!

https://github.com/mailgun/godebug

Quer saber mais sobre o godebug? Aqui está uma palestra que dei no GoSF, incluindo mais detalhes sobre geração de código e gerenciamento de concorrência: