Linux下用内存管理器的钩子函数跟踪内存泄漏

作为Linux下的C程序员,我总是习惯在单元测试通过之后,再用valgrind把程序跑一下,看看有没有内存泄漏和内存越界等问题。可惜的是,有时valgrind并不能很好的工作,像基于DirectFB的多进程程序在valgrind下是跑不起的, 这时我们可以通过内存管理器的钩子函数来跟踪内存泄漏。

glibc提供的内存管理器的钩子函数让你可以监控/改变内存管理函数的行为。其实glibc已经利用这个机制实现了内存泄漏检测的功能,提供了mtrace/muntrace两个函数和mtrace工具,只是不太好用,一是速度慢,二是没有backtrace。更惨的是在Fedora 7上再也找不到它了,只好自己写一个: 

先记录分配/释放操作:

/**//*memory_trace.c*/

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

static void  memory_trace_init(void);
static void  memory_trace_deinit(void);
static void *my_malloc_hook (size_t size, const void* ptr);
static void  my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);

static void *my_malloc_hook (size_t size, const void* ptr);
static void  my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);

static void *(*old_malloc_hook)(size_t size, const void* ptr);
static void  (*old_free_hook)(void* ptr, const void* caller);
static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);

#define BACK_TRACE_DEPTH 8
#define CACHE_SIZE       512

static FILE* g_memory_trace_fp = NULL;
static int   g_memory_trace_cache_used = 0;
/**//*additional 3 items: alloc/free addr size*/
static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3];
static void  memory_trace_flush(void);
static void  memory_trace_write(int alloc, void* addr, int size);

static void memory_trace_backup(void)
...{
    old_malloc_hook  = __malloc_hook;
    old_free_hook    = __free_hook;
    old_realloc_hook = __realloc_hook;

return;
}

static void memory_trace_hook(void)
...{
    __malloc_hook  = my_malloc_hook;
    __free_hook    = my_free_hook;
    __realloc_hook = my_realloc_hook;

return;
}

static void memory_trace_restore(void)
...{
    __malloc_hook  = old_malloc_hook;
    __free_hook    = old_free_hook;
    __realloc_hook = old_realloc_hook;

return;
}

static void memory_trace_init(void)
...{
    if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE") != NULL)
    ...{
        char file_name[260] = ...{0};
        snprintf(file_name, sizeof(file_name), "/tmp/%d_memory.log", getpid());
        if((g_memory_trace_fp = fopen(file_name, "wb+")) != NULL)
        ...{
            memory_trace_backup();
            memory_trace_hook();
        }

atexit(memory_trace_deinit);
    }

return;
}

static void memory_trace_deinit(void)
...{
    if(g_memory_trace_fp != NULL)
    ...{
        memory_trace_restore();
        memory_trace_flush();
        fclose(g_memory_trace_fp);
        g_memory_trace_fp = NULL;
    }

return;
}

void (*__malloc_initialize_hook) (void) = memory_trace_init;

static void * my_malloc_hook (size_t size, const void *caller)
...{
    void *result = NULL;
    memory_trace_restore();
    result = malloc (size);
    memory_trace_write(1, result, size);
    memory_trace_hook();
   
    return result;
}

static void my_free_hook (void *ptr, const void *caller)
...{
    memory_trace_restore();
    free (ptr);
    memory_trace_write(0, ptr, 0);
    memory_trace_hook();

return;
}

static void *my_realloc_hook (void *ptr, size_t size, const void *caller)
...{
    void* result = NULL;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwpjdd.html