Seja Elegante

Talvez possa ser algo que passe despercebido por vários desenvolvedores, porém dúvido quem nunca tenha se deparado com mensagens de “log” vagas ou sem nenhum nexo ou bem pior, como mensagens idênticas replicadas por várias partes do código! :shock:
A algum tempo atrás estava trabalhando em um projeto em que um respectivo desenvolvedor da equipe possuia o costume de espalhar ou replicar centenas de printf() pelo código com mensagens tipo!

printf(“!!! FULANO – Aqui!!!”);

ou

printf(“!!! FULANO – Arquivo.cpp:  Aqui!!!\n”);

Precisa falar que isto e pessímo? Certo que e preciso um bom senso com o uso de mensagens de depuração pois dependendo ao invés de ajudar podem só atrapalhar. principalmente quando você necessita ter controle sobre tais mensagens, como por exemplo obter a localização da mensagem através do nome da função, arquivo e linha.

Lembrando que dependendo da situação e contexto eu sou a favor de utilizar o pragma no compilador ativando o “poison” desativando dentre várias funções o printf (), evitando de ter espalhada pelo sistema! Futuramente um post sobre o poison.

O pré-processador do GCC oferece (*) várias macros, as que irei utilizar em questão são:

  • __FILE__ : Substituída pelo nome do arquivo.
  • __PRETTY_FUNCTION__ : Substituídas pelo nome nome da função.
  • __LINE__ : Substituída pelo número da linha de código.

(*) Mais detalhes sobre as macros do GCC.

A utilização dessas macros irá facilitar a localização das mensagens de depuração, Abaixo um simples exemplo demonstrado em debug1.c.

/**
 * By Jorge Pereira <jpereiran@gmail.com>
 * Date: Tue Jun 29 02:51:44 BRT 2010
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

// Macro condicional
#ifdef DEBUG
#	warning "Debug is enabled"
#	define TRACE_DEBUG(fmt, ...) \
	fprintf(stderr, " ** DEBUG: %s:%d %s(): "fmt"\n", \
	__FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)
#else
#	warning "Debug is disabled"
#	define TRACE_DEBUG(fmt, ...)
#endif

#define MAX_STRING 5 /* tamanho maximo de uma string */

// Função simples de exemplo...
void
show_name (const char* name,
		   size_t len)
{
	size_t offset = len;

	// Verificação qualquer...
	if (len > MAX_STRING)
	{
		// Abaixo mensagem de depuração, perceba que ela será ativada apenas
                // quando em tempo de compilação você ativar a macro "DEBUG"
		TRACE_DEBUG ("Ooops! Tamanho da string(%d) e excedeu o limite(%d)...",
			len, MAX_STRING);
		offset = MAX_STRING;
	}

	// Manipulação qualquer...
	write (STDOUT_FILENO, name, offset);
	write (STDOUT_FILENO, "\n", 1);
}

int
main (int argc, char* argv[])
{
	if (argc < 2)
	{
		printf ("Usage: %s <string>\n", argv[0]);
		exit (1);
	}

	show_name (argv[1], strlen (argv[1]));

	return 0;
}

Com este exemplo básico em mãos, vamos compilar e executar.

[jpereira@miracleworld Codes]$ gcc -Wall -o debug1 debug1.c
debug1.c:18:3: warning: #warning "Debug is disabled"
[jpereira@miracleworld Codes]$ ./debug1 "Jorge"
Jorge
[jpereira@miracleworld Codes]$ ./debug1 "Jorge Pereira"
Jorge
[jpereira@miracleworld Codes]$

Imagine que seu sistema possui inúmeros arquivos e você precisa em determinados pontos obter o máximo de informação sobre as mensagens de depuração, talvez seja interessante ter além de uma mensagem objetiva obter juntamente o nome da função, arquivo e linha de onde foi invocada tal mensagem.

[jpereira@miracleworld Codes]$ gcc -Wall -DDEBUG -o debug1 debug1.c
debug1.c:13:3: warning: #warning "Debug is enabled"
[jpereira@miracleworld Codes]$ ./debug1 "Jorge"
Jorge
[jpereira@miracleworld Codes]$ ./debug1 "Jorge Pereira"
 ** DEBUG: debug1.c:36 show_name(): Ooops! Tamanho da string(13) e excedeu o limite(5)...
Jorge
[jpereira@miracleworld Codes]$

Agora veja situação que você está trabalhando em um sistema com várias checagens complexas de cenários “nada convencionais” em que você não tem certeza se vai acontecer sempre ou não.
Sem falar que torna-se um atrativo a possibilidade de você poder ativar/desativar o simples mecanismo, como por exemplo poder criar macros para depuração de componentes específicos (ex.: relacionadas a sockets, I/O, sgdb, …), isto poderá facilitar a identificação e remoção de bugs.

Lembrando que este e o primeiro post entre vários que pretendo publicar relacionados a sugestões e técnicas de depuração. Fique de olho na segunda parte desta saga em que irei explicar uma mais dinâmica do mesmo assunto abordado neste post associando ao uso de variáveis de ambiente.
Dúvidas e Sugestões, são sempre bem vindas! ;)

Autor: Jorge Pereira