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:
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.
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.EnterFuncinforma à 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.ExitFuncinforma à biblioteca de tempo de execução que estamos saindo de uma função. Omitido emmain. - O
godebug.Linefaz 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.Declareregistra 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) pkgA, pkgB, pkgC e pkg/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: