Antes de começarmos a falar sobre debugging de código, é importante falarmos sobre o responsável por sua criação, o bug. Mas afinal, o que é um bug? Em linguagem computacional, um bug é um erro num programa, ou sistema, que produz um resultado incorreto ou inesperado. Mas bug, em inglês, significa inseto. Então, qual a relação entre insetos com erros computacionais?
O termo bug, usado para descrever erros técnicos, remonta, pelo menos, do ano de 1878, pelas palavras do grande inventor Thomas Edison, mas é mais comumente lembrado e associado à Grace Hopper (uma analista de sistemas da Marinha dos Estados Unidos) que, juntamente com sua equipe, descobriu um erro computacional causado por um inseto (uma traça) que havia ficado preso em um dispositivo de controle de intensidade de corrente elétrica, num circuito do computador Mark II.
Os operadores que o encontraram eram familiares com o termo (bug) de engenharia e guardaram o inseto com a nota: “Primeiro caso real de um bug a ser encontrado.”. Daí em diante, quando havia erros, ou falhas computacionais, Grace Hopper e sua equipe atribuíam os mesmos a um possível inseto, ou bug.
Este termo pegou e ficou até os nossos dias.
Agora que sabemos como surgiu o termo bug, fica mais fácil de entendermos de onde vem o termo debugging. No inglês, o “de” inserido no prefixo de uma palavra, pode indicar o seu antônimo, por exemplo: attached/detached (anexar/separar), compile/decompile (compilar/descompilar), etc.
Dito isso, temos que a definição do termo debugging, ou depuração, em português, é o processo de localizar e remover bugs (ou erros) em código de uma linguagem computacional ou hardware. Neste artigo vamos focar na parte do software.
Existem diversas formas ou técnicas de se fazer o debugging de um programa, algumas mais simples e outras mais complexas. Sendo assim, vamos começar por um procedimento bastante simples para entender o conceito base e depois vamos avançando.
Para realizar o debugging de um programa, podemos utilizar o seguinte método:
- Identificar: Primeiro, temos que identificar um problema que faz com que o programa não funcione da maneira esperada, causando erros;
- Isolar: O segundo passo é identificar e isolar a fonte deste problema, ou seja, sabemos que existe um problema, mas em que parte(s) do programa ele ocorre?
- Resolver: Por fim, consertar o código causador do problema e voltar para o passo um, até que não existam mais problemas.
Este é um processo necessário em praticamente todo o desenvolvimento de software. Quanto mais complexo é um programa, maior a chance de existirem erros e de se ter a necessidade de fazer o debugging dos mesmos. Quando identificados vários problemas num mesmo programa, é aconselhável realizar a priorização destes, ou seja, ordenar os bugs, do mais prioritário para o menos prioritário, para que os mais problemáticos sejam resolvidos primeiro. Uma correta priorização pode diminuir bastante o tempo de correção dos erros, já que alguns destes erros podem causar outros em várias partes do código.
Estatisticamente, devemos saber que a grande maioria dos bugs é causada por pessoas, tanto no desenvolvimento do código quanto no design do programa, e muito raramente irá acontecer de existirem erros causados, por exemplo, por compiladores que produzem código incorreto, ou pelo framework com o qual se desenvolve (o que não deixaria de ser uma falha humana, na construção do compilador, ou do framework).
Conforme a experiência demonstra, na maioria das vezes em que é encontrado um erro e este é dado como sendo da plataforma, do framework, ou mesmo da IDE, raras serão as vezes onde este erro não será, na verdade, do programador que desenvolveu o programa.
Então, antes de atribuir um bug para um terceiro, devemos procurar da melhor maneira possível pela verdadeira causa do problema.
Não é incomum o fato de um programador passar mais tempo procurando e resolvendo bugs de seus programas do que desenvolvendo o código. Daí a importância de se compreender, o melhor possível, as ferramentas que ajudem a diminuir este custo. Tais ferramentas são conhecidas como debuggers e é sobre elas que vamos falar neste artigo.
Primeiro contato
Antes de começarmos a procurar por erros com a ajuda de um debugger, temos uma opção para fazer o debugging de maneira rápida e simples, sendo por vezes a única alternativa disponível, como acontece no caso das aplicações distribuídas, onde pode não ser possível a ligação de uma ferramenta de debugging com o programa em execução.
Inserir instruções de log no código é um método relativamente simples de torná-lo depurável e, por isso, um bom planejamento do log a ser inserido no código pode evitar muitas dores de cabeça e ajudar em muito na procura das causas e resolução dos problemas.
Existem muitas ferramentas e frameworks para ajudá-lo a fazer o logging de um programa, sendo que, para a linguagem Java, duas das mais conhecidas e utilizadas são o java.util.logging e o bom e velho Log4j. Apesar de a primeira opção seguir melhorando consideravelmente a cada nova versão, o Log4j oferece uma série de funcionalidades ainda não existentes no java.util.logging.
Uma das funcionalidades mais interessantes do Log4j é a flexibilidade do seu PatternLayout, que nos permite descrever praticamente o que quisermos ver no log em termos de valores de variáveis, datas e formatação dos dados (quer o log seja em arquivo, console, e-mail, etc.) através da definição de padrões descritos nos arquivos de configuração da ferramenta. Outra funcionalidade interessante e muito útil do Log4j é a facilidade de podermos enviar e-mails com a descrição de erros utilizando o SMTPHandler, onde podemos definir quais os erros que queremos que nos sejam enviados por e-mail e quais devem ser logados em outros meios, de forma a separarmos a informação essencial para entendermos o problema, de toda a informação necessária para executar sua correção no código.
Através da ...