Ir para o conteúdo

MemDebug

Se têm um programa complexo, e o gdb diz que estão a libertar memoria não alocada.

Usem temporariamente estas funcoes de alocação de memória para ajudar no debug. Usa btrees para ser um mais rápido do que listas, mas cuidado, usem só isto para debug, porque a performance deve ser afectada significativamente se forem feitas muitas alocações de memória.

Ficheiro: memdebug.h

/*
 *  GPL
 *
 *  Written by Diogo Sousa aka orium
 *  Copyright (C) 2008 Diogo Sousa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef MEMDEBUG_H_
#define MEMDEBUG_H_

#if 1
#  define MEM_DEBUG 1
#  warning Memory debug ON

#  include <stdio.h>
#  include <types.h>

extern void *debug_malloc(size_t);
extern void *debug_realloc(void *, size_t);
extern void *debug_calloc(size_t, size_t);
extern void *debug_strdup(const char *);
extern void debug_free(void *);
extern void debug_fake_malloc(void *);
extern void debug_print_alloced_memory(FILE *);
extern void print_backtrace(void);

#  define free(p) debug_free(p)
#  define malloc(s) debug_malloc(s)
#  define realloc(p,s) debug_realloc(p,s)
#  define calloc(e,s) debug_calloc(e,s)
#  define strdup(p) debug_strdup(p);
#  define ndb_free(p) ({debug_fake_malloc(p); free(p);})

#else
#  define MEM_DEBUG 0
#  define ndb_free(p) free(p)
#endif

#endif

Ficheiro: memdebug.c

/*
 *  GPL
 *
 *  Written by Diogo Sousa aka orium
 *  Copyright (C) 2008 Diogo Sousa
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <search.h>
#include <execinfo.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#define BACKTRACE_MEMORY 512

struct meminfo
{
        void *addr;
        size_t size;
};

inline static void memerror(void *);
int cmp_mem_pointer(const void *, const void *);
void debug_print_mem_tree_node(const void *, const VISIT, const int __attribute__((unused)));
void debug_print_alloced_memory(FILE *);
void *debug_malloc(size_t);
void *debug_realloc(void *, size_t);
void *debug_calloc(size_t, size_t);
void *debug_strdup(const char *);
void debug_free(void *);
void debug_fake_malloc(void *);
void print_backtrace(void);
void debug_abort(int);

void *mem_root=NULL;
static FILE *debug_output;

inline static void memerror(void *p)
{
        if (p == NULL)
        {
                fprintf(stderr,"Error: Cannot allocate memory\n");

                exit(-1);
        }
}

int cmp_mem_pointer(const void *p1, const void *p2)
{
        const struct meminfo *m1=p1;
        const struct meminfo *m2=p2;

        return ((int)m1->addr)-((int)m2->addr);
}

void debug_print_mem_tree_node(const void *nodep, const VISIT which, const int depth  __attribute__((unused)))
{
        if (which == postorder || which == leaf)
        {
                struct meminfo *node=*(struct meminfo **)nodep;

                if (node->size)
                        fprintf(debug_output,"%p: %d\n",node->addr,(int)node->size);
                else
                        fprintf(debug_output,"%p: ?\n",node->addr);
        }
}

void debug_print_alloced_memory(FILE *output)
{
        debug_output=output;

        twalk(mem_root,debug_print_mem_tree_node);
}

void *debug_malloc(size_t size)
{
        struct meminfo *mem;
        void *z;
        void *ret;

        ret=malloc(size);

        if (ret == NULL)
                return NULL;

        mem=malloc(sizeof(*mem));
        memerror(mem);

        mem->addr=ret;
        mem->size=size;

        z=tsearch(mem,&mem_root,cmp_mem_pointer);
        memerror(z);

        return ret;
}

void *debug_realloc(void *p, size_t size)
{
        struct meminfo mem;
        struct meminfo **pneedle;
        struct meminfo *needle;
        void *z;

        mem.addr=p;

        pneedle=tfind(&mem,&mem_root,cmp_mem_pointer);

        if (pneedle == NULL)
        {
                fprintf(stderr,"DEBUG realloc(): Endereço %p não foi alocado\n",p);
                print_backtrace();
        }

        needle=*pneedle;

        tdelete(needle,&mem_root,cmp_mem_pointer);

        needle->addr=realloc(needle->addr,size);

        if (needle->addr == NULL)
        {
                free(needle);

                return NULL;
        }

        needle->size=size;

        z=tsearch(needle,&mem_root,cmp_mem_pointer);
        memerror(z);

        return needle->addr;
}

void *debug_calloc(size_t n, size_t size)
{
        struct meminfo *mem;
        void *z;
        void *ret;

        ret=calloc(n,size);

        if (ret == NULL)
                return NULL;

        mem=malloc(sizeof(*mem));
        memerror(mem);

        mem->addr=ret;
        mem->size=size*n;

        z=tsearch(mem,&mem_root,cmp_mem_pointer);
        memerror(z);

        return ret;
}

void *debug_strdup(const char *str)
{
        struct meminfo *mem;
        void *z;
        void *ret;

        ret=strdup(str);

        if (ret == NULL)
                return NULL;

        mem=malloc(sizeof(*mem));
        memerror(mem);

        mem->addr=ret;
        mem->size=strlen(str)+1;

        z=tsearch(mem,&mem_root,cmp_mem_pointer);
        memerror(z);

        return ret;
}

void debug_free(void *p)
{
        struct meminfo mem;
        struct meminfo **pneedle;
        struct meminfo *needle;

        if (p == NULL)
                return;

        mem.addr=p;

        pneedle=tfind(&mem,&mem_root,cmp_mem_pointer);

        if (pneedle == NULL)
        {
                fprintf(stderr,"DEBUG free(): Endereço %p não foi alocado\n",p);
                print_backtrace();
        }

        needle=*pneedle;

        tdelete(needle,&mem_root,cmp_mem_pointer);

        assert(needle->addr == p);

        free(needle->addr);
        free(needle);
}

void debug_fake_malloc(void *p)
{
        struct meminfo *mem;
        void *z;

        mem=malloc(sizeof(*mem));
        memerror(mem);

        mem->addr=p;
        mem->size=0;

        z=tsearch(mem,&mem_root,cmp_mem_pointer);
        memerror(z);
}

void print_backtrace(void)
{
        void *btbuf[BACKTRACE_MEMORY];
        char **strbuf;
        int i;
        int z;

        z=backtrace(btbuf,BACKTRACE_MEMORY);

        strbuf=backtrace_symbols(btbuf,z);

        fprintf(stderr,"\tBacktrace:\n");

        for (i=1; i < z; i++)
                fprintf(stderr,"\t  %s\n",strbuf[i]);

        free(strbuf);

        debug_abort(SIGTERM);
}

void debug_abort(int sig_num)
{
#if 1
        fprintf(stderr,"Raising SIGTERM for debugging purposes\n");
        raise(sig_num);
#else
        exit(0);
#endif
}

Exemplo de uso

Importante: Adicionar exemplo de uso devidamente comentado.