您的位置:首页 > 理论基础 > 计算机网络

Linux下用内存管理器的钩子函数跟踪内存泄漏 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/200

2012-01-04 20:24 876 查看
作为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;

    memory_trace_restore();

    memory_trace_write(0, ptr, 0);

    result = realloc(ptr, size);

    memory_trace_write(1, result, size);

    memory_trace_hook();

    return result;

}

static void memory_trace_flush_one_entry(int index)

...{

    int offset = 0;

    char buffer[512] = ...{0};

    int fd = fileno(g_memory_trace_fp);

    int alloc  = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH]; 

    void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1]; 

    int size   = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2];

    void** backtrace_buffer = g_memory_trace_cache[index];

    snprintf(buffer, sizeof(buffer), "%s %p %d ", alloc ? "alloc":"free", addr, size);

    if(!alloc)

    ...{

        write(fd, buffer, strlen(buffer));

        return;

    }

    

    char** symbols = backtrace_symbols(backtrace_buffer, BACK_TRACE_DEPTH);

    if(symbols != NULL)

    ...{

        int i = 0;

        offset = strlen(buffer);

        for(i = 0; i < BACK_TRACE_DEPTH; i++)

        ...{

            if(symbols[i] == NULL)

            ...{

                break;

            }

            char* begin = strchr(symbols[i], '(');

            if(begin != NULL)

            ...{

                *begin = ' ';

                char* end = strchr(begin, ')');

                if(end != NULL)

                ...{

                    strcpy(end, " ");

                }

                strncpy(buffer+offset, begin, sizeof(buffer)-offset);

                offset += strlen(begin);

            }

        }

        write(fd, buffer, offset);

        free(symbols);

    }

    return;

}

static void memory_trace_flush(void)

...{

    int i = 0;

    for(i = 0; i < g_memory_trace_cache_used; i++)

    ...{

        memory_trace_flush_one_entry(i);

    }

    g_memory_trace_cache_used = 0;

    return;

}

static void memory_trace_write(int alloc, void* addr, int size)

...{

    if(g_memory_trace_cache_used >= CACHE_SIZE)

    ...{

        memory_trace_flush();

    }

    int i = 0;

    void* backtrace_buffer[BACK_TRACE_DEPTH] = ...{0};

    backtrace(backtrace_buffer, BACK_TRACE_DEPTH);

    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][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+2] = (void*)size;

    g_memory_trace_cache_used++;

    return;

}

#ifdef MEMORY_TRACE_TEST

void test(void)

...{

    char* p = malloc(100);

    p = malloc(123);

    free(p);

    return;

}

int main(int argc, char* argv[])

...{

    malloc(100);

    test();

    malloc(100);

    test();

    char* p = malloc(100);

    free(p);

    

    return 0;

}

#endif/*MEMORY_TRACE_TEST*/

把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。

再写个程序来分析log文件:

/**//*mtrace.c*/

#include <stdio.h>

#define MAX_ENTRY 1024*1024

int main(int argc, char* argv[])

...{

    if(argc != 3)

    ...{

        printf("usage: %s [log file] [out file] ", argv[0]);

        return 0;

    }

    FILE* fp     = fopen(argv[1], "r");

    FILE* fp_out = fopen(argv[2], "wb+");

    if(fp == NULL || fp_out == NULL)

    ...{

        printf("open file failed ");

        if(fp != NULL)

        ...{

            fclose(fp);

        }

        if(fp_out != NULL)

        ...{

            fclose(fp_out);

        }

        return;

    }

    int   i = 0;

    int   n = 0;

    int   skip = 0;

    int   line_index = 0;

    void* addr = 0;

    char  line[260] = ...{0};

    void* addrs_array[MAX_ENTRY] = ...{0};

    int   lines_array[MAX_ENTRY] = ...{0};

    while(fgets(line, sizeof(line), fp) != NULL)

    ...{

        if(line[0] != 'a' && line[0] != 'f')

        ...{

            line_index++;

            continue;

        }

        addr = NULL;

        if(strncmp(line, "alloc", 5) == 0 && n < MAX_ENTRY)

        ...{

            sscanf(line, "alloc %p", &addr);

            addrs_array
= addr;

            lines_array
= line_index;

            n++;

            

            printf("a");

        }

        else if(strncmp(line, "free", 4) == 0)

        ...{

            sscanf(line, "free %p", &addr);

            for(i = 0; i < n; i++)

            ...{

                if(addrs_array[i] == addr)

                ...{

                    lines_array[i] = -1;

                    break;

                }

            }

            printf("f");

        }

        line_index++;

    }

    printf(" ");

    fseek(fp, 0, 0);

    i = 0;

    line_index = 0;

    while(fgets(line, sizeof(line), fp) != NULL)

    ...{

        if(strncmp(line, "alloc", 5) == 0)

        ...{

            if(lines_array[i] == line_index)

            ...{

                printf("leak %s", line);

                fprintf(fp_out, "*");

                skip = 0;

                i++;

            }

            else

            ...{

                skip = 1;

            }

        }

        else if(strncmp(line, "free", 4) == 0)

        ...{

            skip = 1;

        }

        if(!skip)

        ...{

            fputs(line, fp_out);

        }

        line_index++;

    }

    fclose(fp);

    fclose(fp_out);

    return 0;

}

把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。

~~end~~

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2009-12/23575.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux hook cache buffer fp null
相关文章推荐