用内存管理器的钩子函数跟踪内存泄漏
<!--@page { size: 21cm 29.7cm; margin: 2cm }P { margin-bottom: 0.21cm }-->用内存管理器的钩子函数跟踪内存泄漏载时请注明出处和作者联系方式作者联系方式:李先静作为Linux下的C程序员,我总是习惯在单元测试通过之后,再用valgrind把程序跑一下,看看有没
   ·  
 用内存管理器的钩子函数跟踪内存泄漏
载时请注明出处和作者联系方式
  作者联系方式:李先静 <xianjimli at hotmail dot com>
作为Linux下的C程序员,我总是习惯在单元测试通过之后,再用valgrind把程序跑一下,看看有没有内存泄漏和内存越界等问题。可惜的是,有时valgrind并不能很好的工作,像基于DirectFB的多进程程序在valgrind下是跑不起的, 这时我们可以通过内存管理器的钩子函数来跟踪内存泄漏。
glibc提供的内存管理器的钩子函数让你可以监控/改变内存管理函数的行为。其实glibc已经利用这个机制实现了内存泄漏检测的功能,提供了mtrace/muntrace两个函数和mtrace工具,只是不太好用,一是速度慢,二是没有backtrace。更惨的是在Fedora 7上再也找不到它了,只好自己写一个:
先记录分配/释放操作:
 /*memory_trace.c*/
/*memory_trace.c*/
 #include <execinfo.h>
#include <execinfo.h> #include <stdio.h>
#include <stdio.h> #include <stdlib.h>
#include <stdlib.h> #include <sys/types.h>
#include <sys/types.h> #include <unistd.h>
#include <unistd.h> #include <malloc.h>
#include <malloc.h> #include <sys/stat.h>
#include <sys/stat.h> #include <fcntl.h>
#include <fcntl.h> #include <string.h>
#include <string.h>
 static void  memory_trace_init(void);
static void  memory_trace_init(void); static void  memory_trace_deinit(void);
static void  memory_trace_deinit(void); static void *my_malloc_hook (size_t size, const void* ptr);
static void *my_malloc_hook (size_t size, const void* ptr); static void  my_free_hook (void* ptr, const void* caller);
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_realloc_hook (void *ptr, size_t size, const void *caller);
 static void *my_malloc_hook (size_t size, const void* ptr);
static void *my_malloc_hook (size_t size, const void* ptr); static void  my_free_hook (void* ptr, const void* caller);
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_realloc_hook (void *ptr, size_t size, const void *caller);
 static void *(*old_malloc_hook)(size_t size, const void* ptr);
static void *(*old_malloc_hook)(size_t size, const void* ptr); static void  (*old_free_hook)(void* ptr, const void* caller);
static void  (*old_free_hook)(void* ptr, const void* caller); static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
 #define BACK_TRACE_DEPTH 8
#define BACK_TRACE_DEPTH 8 #define CACHE_SIZE       512
#define CACHE_SIZE       512
 static FILE* g_memory_trace_fp = NULL;
static FILE* g_memory_trace_fp = NULL; static int   g_memory_trace_cache_used = 0;
static int   g_memory_trace_cache_used = 0; /*additional 3 items: alloc/free addr size*/
/*additional 3 items: alloc/free addr size*/ static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3];
static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3];  static void  memory_trace_flush(void);
static void  memory_trace_flush(void); static void  memory_trace_write(int alloc, void* addr, int size);
static void  memory_trace_write(int alloc, void* addr, int size);
 static void memory_trace_backup(void)
static void memory_trace_backup(void) {
{ old_malloc_hook  = __malloc_hook;
    old_malloc_hook  = __malloc_hook; old_free_hook    = __free_hook;
    old_free_hook    = __free_hook; old_realloc_hook = __realloc_hook;
    old_realloc_hook = __realloc_hook;
 return;
    return; }
}
 static void memory_trace_hook(void)
static void memory_trace_hook(void) {
{ __malloc_hook  = my_malloc_hook;
    __malloc_hook  = my_malloc_hook; __free_hook    = my_free_hook;
    __free_hook    = my_free_hook; __realloc_hook = my_realloc_hook;
    __realloc_hook = my_realloc_hook;
 return;
    return; }
}
 static void memory_trace_restore(void)
static void memory_trace_restore(void) {
{ __malloc_hook  = old_malloc_hook;
    __malloc_hook  = old_malloc_hook; __free_hook    = old_free_hook;
    __free_hook    = old_free_hook; __realloc_hook = old_realloc_hook;
    __realloc_hook = old_realloc_hook;
 return;
    return; }
}
 static void memory_trace_init(void)
static void memory_trace_init(void) {
{ if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE") != NULL)
    if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE") != NULL) {
    { char file_name[260] = {0};
        char file_name[260] = {0}; snprintf(file_name, sizeof(file_name), "/tmp/%d_memory.log", getpid());
        snprintf(file_name, sizeof(file_name), "/tmp/%d_memory.log", getpid()); if((g_memory_trace_fp = fopen(file_name, "wb+")) != NULL)
        if((g_memory_trace_fp = fopen(file_name, "wb+")) != NULL) {
        { memory_trace_backup();
            memory_trace_backup(); memory_trace_hook();
            memory_trace_hook(); }
        }
 atexit(memory_trace_deinit);
        atexit(memory_trace_deinit); }
    }
 return;
    return; }
}
 static void memory_trace_deinit(void)
static void memory_trace_deinit(void) {
{ if(g_memory_trace_fp != NULL)
    if(g_memory_trace_fp != NULL) {
    { memory_trace_restore();
        memory_trace_restore(); memory_trace_flush();
        memory_trace_flush(); fclose(g_memory_trace_fp);
        fclose(g_memory_trace_fp); g_memory_trace_fp = NULL;
        g_memory_trace_fp = NULL; }
    }
 return;
    return; }
}
 void (*__malloc_initialize_hook) (void) = memory_trace_init;
void (*__malloc_initialize_hook) (void) = memory_trace_init;
 static void * my_malloc_hook (size_t size, const void *caller)
static void * my_malloc_hook (size_t size, const void *caller) {
{ void *result = NULL;
    void *result = NULL; memory_trace_restore();
    memory_trace_restore(); result = malloc (size);
    result = malloc (size); memory_trace_write(1, result, size);
    memory_trace_write(1, result, size); memory_trace_hook();
    memory_trace_hook(); 
     return result;
    return result; }
}
 static void my_free_hook (void *ptr, const void *caller)
static void my_free_hook (void *ptr, const void *caller) {
{ memory_trace_restore();
    memory_trace_restore(); free (ptr);
    free (ptr); memory_trace_write(0, ptr, 0);
    memory_trace_write(0, ptr, 0); memory_trace_hook();
    memory_trace_hook();
 return;
    return; }
}
 static void *my_realloc_hook (void *ptr, size_t size, const void *caller)
static void *my_realloc_hook (void *ptr, size_t size, const void *caller) {
{ void* result = NULL;
    void* result = NULL;
 memory_trace_restore();
    memory_trace_restore(); memory_trace_write(0, ptr, 0);
    memory_trace_write(0, ptr, 0); result = realloc(ptr, size);
    result = realloc(ptr, size); memory_trace_write(1, result, size);
    memory_trace_write(1, result, size); memory_trace_hook();
    memory_trace_hook();
 return result;
    return result; }
}
 static void memory_trace_flush_one_entry(int index)
static void memory_trace_flush_one_entry(int index) {
{ int offset = 0;
    int offset = 0; char buffer[512] = {0};
    char buffer[512] = {0}; int fd = fileno(g_memory_trace_fp);
    int fd = fileno(g_memory_trace_fp); int alloc  = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];
    int alloc  = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];  void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];
    void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];  int size   = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2];
    int size   = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2]; void** backtrace_buffer = g_memory_trace_cache[index];
    void** backtrace_buffer = g_memory_trace_cache[index];
 snprintf(buffer, sizeof(buffer), "%s %p %d ", alloc ? "alloc":"free", addr, size);
    snprintf(buffer, sizeof(buffer), "%s %p %d ", alloc ? "alloc":"free", addr, size); if(!alloc)
    if(!alloc) {
    { write(fd, buffer, strlen(buffer));
        write(fd, buffer, strlen(buffer)); return;
        return; }
    } 
     char** symbols = backtrace_symbols(backtrace_buffer, BACK_TRACE_DEPTH);
    char** symbols = backtrace_symbols(backtrace_buffer, BACK_TRACE_DEPTH); if(symbols != NULL)
    if(symbols != NULL) {
    { int i = 0;
        int i = 0; offset = strlen(buffer);
        offset = strlen(buffer); for(i = 0; i < BACK_TRACE_DEPTH; i++)
        for(i = 0; i < BACK_TRACE_DEPTH; i++) {
        { if(symbols[i] == NULL)
            if(symbols[i] == NULL) {
            { break;
                break; }
            } char* begin = strchr(symbols[i], '(');
            char* begin = strchr(symbols[i], '('); if(begin != NULL)
            if(begin != NULL) {
            { *begin = ' ';
                *begin = ' '; char* end = strchr(begin, ')');
                char* end = strchr(begin, ')'); if(end != NULL)
                if(end != NULL) {
                { strcpy(end, " ");
                    strcpy(end, " "); }
                } strncpy(buffer+offset, begin, sizeof(buffer)-offset);
                strncpy(buffer+offset, begin, sizeof(buffer)-offset); offset += strlen(begin);
                offset += strlen(begin); }
            } }
        } write(fd, buffer, offset);
        write(fd, buffer, offset); free(symbols);
        free(symbols); }
    }
 return;
    return; }
}
 static void memory_trace_flush(void)
static void memory_trace_flush(void) {
{ int i = 0;
    int i = 0; for(i = 0; i < g_memory_trace_cache_used; i++)
    for(i = 0; i < g_memory_trace_cache_used; i++) {
    { memory_trace_flush_one_entry(i);
        memory_trace_flush_one_entry(i); }
    } g_memory_trace_cache_used = 0;
    g_memory_trace_cache_used = 0;
 return;
    return; }
}
 static void memory_trace_write(int alloc, void* addr, int size)
static void memory_trace_write(int alloc, void* addr, int size) {
{ if(g_memory_trace_cache_used >= CACHE_SIZE)
    if(g_memory_trace_cache_used >= CACHE_SIZE) {
    { memory_trace_flush();
        memory_trace_flush(); }
    }
 int i = 0;
    int i = 0; void* backtrace_buffer[BACK_TRACE_DEPTH] = {0};
    void* backtrace_buffer[BACK_TRACE_DEPTH] = {0}; backtrace(backtrace_buffer, BACK_TRACE_DEPTH);
    backtrace(backtrace_buffer, BACK_TRACE_DEPTH);
 for(i = 0; i < BACK_TRACE_DEPTH; i++)
    for(i = 0; i < BACK_TRACE_DEPTH; i++) {
    { g_memory_trace_cache[g_memory_trace_cache_used][i] = backtrace_buffer[i];
        g_memory_trace_cache[g_memory_trace_cache_used][i] = backtrace_buffer[i]; }
    } g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH] = (void*)alloc;
    g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH] = (void*)alloc; g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+1] = addr;
    g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+1] = addr; g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+2] = (void*)size;
    g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+2] = (void*)size;
 g_memory_trace_cache_used++;
    g_memory_trace_cache_used++;
 return;
    return; }
}
 #ifdef MEMORY_TRACE_TEST
#ifdef MEMORY_TRACE_TEST void test(void)
void test(void) {
{ char* p = malloc(100);
    char* p = malloc(100); p = malloc(123);
    p = malloc(123);
 free(p);
    free(p);
 return;
    return; }
} int main(int argc, char* argv[])
int main(int argc, char* argv[]) {
{ malloc(100);
    malloc(100); test();
    test(); malloc(100);
    malloc(100); test();
    test(); char* p = malloc(100);
    char* p = malloc(100); free(p);
    free(p); 
     return 0;
    return 0; }
} #endif/*MEMORY_TRACE_TEST*/
#endif/*MEMORY_TRACE_TEST*/把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。
再写个程序来分析log文件:
 /*mtrace.c*/
/*mtrace.c*/ #include <stdio.h>
#include <stdio.h>
 #define MAX_ENTRY 1024*1024
#define MAX_ENTRY 1024*1024
 int main(int argc, char* argv[])
int main(int argc, char* argv[]) {
{ if(argc != 3)
    if(argc != 3) {
    { printf("usage: %s [log file] [out file] ", argv[0]);
        printf("usage: %s [log file] [out file] ", argv[0]); return 0;
        return 0; }
    }
 FILE* fp     = fopen(argv[1], "r");
    FILE* fp     = fopen(argv[1], "r"); FILE* fp_out = fopen(argv[2], "wb+");
    FILE* fp_out = fopen(argv[2], "wb+");
 if(fp == NULL || fp_out == NULL)
    if(fp == NULL || fp_out == NULL) {
    { printf("open file failed ");
        printf("open file failed "); if(fp != NULL)
        if(fp != NULL) {
        { fclose(fp);
            fclose(fp); }
        } if(fp_out != NULL)
        if(fp_out != NULL) {
        { fclose(fp_out);
            fclose(fp_out); }
        } return;
        return; }
    }
 int   i = 0;
    int   i = 0; int   n = 0;
    int   n = 0; int   skip = 0;
    int   skip = 0; int   line_index = 0;
    int   line_index = 0; void* addr = 0;
    void* addr = 0; char  line[260] = {0};
    char  line[260] = {0}; void* addrs_array[MAX_ENTRY] = {0};
    void* addrs_array[MAX_ENTRY] = {0}; int   lines_array[MAX_ENTRY] = {0};
    int   lines_array[MAX_ENTRY] = {0};

 while(fgets(line, sizeof(line), fp) != NULL)
    while(fgets(line, sizeof(line), fp) != NULL) {
    { if(line[0] != 'a' && line[0] != 'f')
        if(line[0] != 'a' && line[0] != 'f') {
        { line_index++;
            line_index++; continue;
            continue; }
        }
 addr = NULL;
        addr = NULL; if(strncmp(line, "alloc", 5) == 0 && n < MAX_ENTRY)
        if(strncmp(line, "alloc", 5) == 0 && n < MAX_ENTRY) {
        { sscanf(line, "alloc %p", &addr);
            sscanf(line, "alloc %p", &addr); addrs_array[n] = addr;
            addrs_array[n] = addr; lines_array[n] = line_index;
            lines_array[n] = line_index; n++;
            n++; 
             printf("a");
            printf("a"); }
        } else if(strncmp(line, "free", 4) == 0)
        else if(strncmp(line, "free", 4) == 0) {
        { sscanf(line, "free %p", &addr);
            sscanf(line, "free %p", &addr); for(i = 0; i < n; i++)
            for(i = 0; i < n; i++) {
            { if(addrs_array[i] == addr)
                if(addrs_array[i] == addr) {
                { lines_array[i] = -1;
                    lines_array[i] = -1; break;
                    break; }
                } }
            }
 printf("f");
            printf("f"); }
        } line_index++;
        line_index++; }
    }
 printf(" ");
    printf(" "); fseek(fp, 0, 0);
    fseek(fp, 0, 0);
 i = 0;
    i = 0; line_index = 0;
    line_index = 0; while(fgets(line, sizeof(line), fp) != NULL)
    while(fgets(line, sizeof(line), fp) != NULL) {
    { if(strncmp(line, "alloc", 5) == 0)
        if(strncmp(line, "alloc", 5) == 0) {
        { if(lines_array[i] == line_index)
            if(lines_array[i] == line_index) {
            { printf("leak %s", line);
                printf("leak %s", line); fprintf(fp_out, "*");
                fprintf(fp_out, "*"); skip = 0;
                skip = 0; i++;
                i++; }
            } else
            else {
            { skip = 1;
                skip = 1; }
            } }
        } else if(strncmp(line, "free", 4) == 0)
        else if(strncmp(line, "free", 4) == 0) {
        { skip = 1;
            skip = 1; }
        }
 if(!skip)
        if(!skip) {
        { fputs(line, fp_out);
            fputs(line, fp_out); }
        }
 line_index++;
        line_index++; }
    }
 fclose(fp);
    fclose(fp); fclose(fp_out);
    fclose(fp_out);
 return 0;
    return 0; }
}
     把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。
~~end~~
 
  更多推荐
 
 

 
    


所有评论(0)