04.08
2020
Vazamento de memória é uma pequena praga que precisa ser combatido. Em alguns sistemas pode não fazer muita diferença, mas quando o processamento envolve um volume grande de dados, ou quando a memória é restrita, como em sistemas embarcados, por exemplo, é necessário tomar muito cuidado.
Pra facilitar a detecção de vazamento de memória podemos fazer uma pequena biblioteca para localizar cada malloc sem seu devido free.
Vamos começar com a estrutura de dados que vai nos permitir gerenciar cada alocação:
typedef struct __memnode {
void *p;
char *file;
unsigned line;
size_t size;
struct __memnode *pnext;
} _memnode;
typedef _memnode* memnode;
Em seguida as funções de alocação e desalocação:
static memnode *lastnode = NULL;
void* _safemalloc(size_t size, char *file, unsigned line) {
void *p = NULL;
memnode *node;
/* memory allocation */
p = malloc(size);
if (!p)
goto err_p;
node = (memnode*)malloc(sizeof(memnode));
if (!node)
goto err_node;
/* fill node */
node->p = p;
node->file = file;
node->line = line;
node->size = size;
/* insert node */
if (lastnode)
node->prev = lastnode;
else
node->prev = NULL;
lastnode = node;
return p;
err_node:
free(p);
err_p:
return NULL;
}
void safefree(void *p) {
memnode *cursor = lastnode;
memnode *prev = cursor;
while (cursor) {
if (cursor->p == p) {
if (cursor == lastnode)
lastnode = cursor->prev;
else
prev->prev = cursor->prev;
free(p);
free(cursor);
break;
}
prev = cursor;
cursor = cursor->prev;
}
}
Primeiro código a ser notado é a variável global static declarada. Assim não é possível acessar a variável através de outros arquivos.
Note também que a função de alocação, _safemalloc, exige três argumentos: o tamanho da alocação, o arquivo e a linha do arquivo. Pra preencher esses dados apropriadamente vamos usar uma macro, assim basta executar safemalloc(size) que o nome do arquivo e o número da linha são preenchidos automaticamente.
#define safemalloc(size) _safemalloc(size, __FILE__, __LINE__)
Vamos fazer também um report de vazamento de memória pra ser executado antes do fim da execução do trecho em que será verificado por vazamento de memória:
void leakreport() {
memnode *cursor = lastnode;
int ct = 1;
if (cursor == NULL) {
printf("NO MEMORY LEAKS DETECTED\n");
return;
}
printf("MEMORY LEAKS DETECTED\n");
while (cursor) {
printf(" LEAK #%d %d bytes: FILE %s:%ld\n",
ct, cursor->size, cursor->file, cursor->line);
cursor = cursor->prev;
ct++;
}
}
Agora só falta um exemplo de uso:
void main() {
char *filename = safemalloc(512);
char *buffer = safemalloc(1024);
safefree(buffer);
leakreport();
}
A saída vai ser a seguinte:
MEMORY LEAKS DETECTED
LEAK #1 512 bytes: FILE C:\Users\labcpp\code\memtrace.c:84
Indicando que houve um vazamento de 512 bytes na linha 84 do arquivo memtrace.c. Note que o caminho do arquivo e o número da linha pode variar.
Em alguns times a utilização de trace de memória é obrigatório, e vazamento de memória é tratado com a criticidade de um bug funcional. Mas cada time é um time, tudo depende do quão crítico é um vazamento de memória.
Comentários
[…] algum tempo no Lab C++ um exemplo de Trace de Alocação em C ANSI, utilizando __LINE__ e __FILE__. Tem um snippet de código muito útil para quem quer evitar […]