内存泄露了好烦,搞个内存泄漏检测插件试试

内存泄漏原因

  1. 代码不带垃圾回收机制,此问题解决方式参考内存池应用。
  2. 动态分配(malloc、new)了内存没用释放(free、delete)

危害

  1. 导致某些内存被系统强制回收
  2. 进程可能被强制kill
  3. malloc或者new等动态内存分配失败

解决方案

如何知道内存泄漏

如上述内存泄漏原因中所说的第二点,动态分配释放未能够成对出现。
那么我们能否对内存分配以及释放,进行监管。

监管内存分配或者释放的两大类方式

  1. 自己定制mlloc、free方案(宏定义)。
    filefuncLINE
  2. 使用builtin_return_address函数进行定位,该接口是系统接口,可以得到当前运行的环境,调用该接口是在地址跳转过去寻找定位,或者前X段调用接口的地址。 随后可以通过addr2line -f -e exe -a address将该地址对应的代码路径读取出来。 ==值得注意的是:==为了通过addr2line将地址对于的代码段读出来,需要做两件事
    2.1 将环境中的随机栈地址disable了
    2.2 gcc编译时候加上参数 -g

手动实现工具

方案一

  1. 使用宏定义,对malloc以及free进行重新定义
  2. 在自定义的malloc 中记录调用malloc的信息,包括调用malloc的当前文件当前行,开辟出来的地址以及空间大小
  3. 在free中将释放掉的空间对应的文件进行删除
void *malloc_hook(size_t size, const char *file, const char *func, int line)
{
    
    
    void *p = malloc(size);
    char str[256] = {
    
    0};
    sprintf(str, "./mem/%p.mem", p);
    FILE *fp = fopen(str, "w");
    fprintf(fp, "[info:]file:%s,func:%s,line:%d, addr:%p,size:%ld\n", file, func, line, p, size);
    fflush(fp);
    fclose(fp);
    return p;
}

void free_hook(void *p, const char *file, const char *func, int line)
{
    
    
    free(p);
    char str[256] = {
    
    0};
    sprintf(str, "./mem/%p.mem", p);
    if (unlink(str) < 0)
    {
    
    
        printf("double free.\n");
    }
}

#define malloc(size) malloc_hook(size, __FILE__, __FUNCTION__, __LINE__)
#define free(p) free_hook(p, __FILE__, __FUNCTION__, __LINE__);

方案二

  1. 使用钩子函数将malloc、free的内容重新定义
  2. 第一步,先将系统函数的malloc、free的实现函数地址进行保存(mem_trace)
  3. 第二步,将为我们自己实现的malloc、free函数地址替换进去
  4. 第三步,将malloc与free的实现函数换回系统自定义的接口(mem_untrace)
  5. 第四步, 由于自己实现的函数中可能会存在开辟空间的动作,为了防止无限回调,需要在进入的时候,就调用mem_untrace切换回系统接口,退出时候,调用mem_trace切换回自己的接口
  6. 第五步,记得在需要debug的那部分位置处加上mem_trace以及mem_untrace
typedef void *(*malloc_hook_t)(size_t size, const void *caller); //系统的__malloc_hook实际的函数类型
malloc_hook_t malloc_f;                                          //用于保存系统默认的__malloc_hook函数地址
typedef void (*free_hook_t)(void *p, const void *caller);        //系统的__free_hook的实际函数类型
free_hook_t free_f;                                              //用于保存系统默认的__free_hook函数指针地址
int replaced = 0;                                                //如果为1,malloc/free指向我们自定义的函数
void mem_trace(void);                                            //让其malloc指向我们自己定义的函数
void mem_untrace(void);                                          //让其free指向我们自己定义的函数
//自定义的malloc函数,与系统的__malloc_hook保持一致
//caller参数代表调用该函数的地址(__builtin_return_address(0)返回的地址就是这个地址)
void *malloc_hook_f(size_t size, const void *caller)
{
    
    
    //防止递归-如果不加这句,会让下面的malloc继续执行malloc_hook_f,从而造成递归
    //我们只要得到caller指针这个值就可以了。
    mem_untrace();
    void *ptr = malloc(size);
    //printf("+%p: addr[%p]\n", caller, ptr);
    char buff[128] = {
    
    0};
    sprintf(buff, "./mem/%p.mem", ptr);
    FILE *fp = fopen(buff, "w");
    fprintf(fp, "[+%p] --> addr:%p, size:%ld\n", caller, ptr, size);
    fflush(fp);
    fclose(fp);  //free
    mem_trace(); //保证下次malloc还是用我们自定义的
    return ptr;
}
void free_hook_f(void *p, const void *caller)
{
    
    
    mem_untrace(); //防止free函数递归
    //printf("-%p: addr[%p]\n", caller, p);
    char buff[128] = {
    
    0};
    sprintf(buff, "./mem/%p.mem", p);
    if (unlink(buff) < 0)
    {
    
     // no exist
        printf("double free: %p\n", p);
        return;
    }
    free(p);
    mem_trace();
}
void mem_trace(void)
{
    
     //mtrace
    replaced = 1;
    malloc_f = __malloc_hook;      //__malloc_hook是系统本身提供的函数指针(会在malloc调用时初始化)
    free_f = __free_hook;          //__free_hook是系统本身提供的(free调用时会初始化)
    __malloc_hook = malloc_hook_f; //指向我们自定义函数,malloc会调用我们定义的函数
    __free_hook = free_hook_f;     //指向我们自定义函数,free会调用我们自定义的函数
}
//
void mem_untrace(void)
{
    
    
    __malloc_hook = malloc_f;
    __free_hook = free_f;
    replaced = 0;
}

测试代码

	mem_trace();
    void *p1 = malloc(10);
    void *p2 = malloc(20);
    free(p1);
    mem_untrace();

方案三

与方案二类似,只是不使用钩子函数,而是自己重新创建malloc与free接口
由于malloc与free的实际开辟释放动作是由==__libc_malloc==、__libc_free完成的,所以我们建立的malloc与free接口只需要实际调用这两个接口就可以了

extern void *__libc_malloc(size_t size); //malloc.h里面定义的
int enable_malloc_hook = 1;              //终止递归的变量(具体参考malloc函数说明)

extern void __libc_free(void *p);
int enable_free_hook = 1;

void *malloc(size_t size)
{
    
    
    if (enable_malloc_hook)
    {
    
     //调用系统
        enable_malloc_hook = 0;
        void *p = __libc_malloc(size); //分配内存,系统的malloc实际上也是调用的这个api进行内存分配。

        //返回malloc调用完成时的地址,可以结合addr2line命令定位到哪一行内存泄露。
        void *caller = __builtin_return_address(0);
        char buff[128] = {
    
    0};
        sprintf(buff, "./mem/%p.mem", p); //使用malloc返回的地址作为文件名(p.mem)
        FILE *fp = fopen(buff, "w");
        fprintf(fp, "[+%p] --> addr:%p, size:%ld\n", caller, p, size);
        fflush(fp);
        //fclose(fp);//注意不能close文件
        //printf函数内部会调用malloc,如果不用enable_malloc_hook变量会导致递归malloc的使用
        printf("malloc :%p\n", p);
        //保证下次调用malloc进入到if,注意,多线程不是线程安全的
        enable_malloc_hook = 1;

        return p;
    }
    else
    {
    
     //如果是其他API(比如printf)调用了malloc,会直接调用__lib_malloc(size_t size)进行分配,从而使得递归得以退出

        return __libc_malloc(size);
    }
}

void free(void *p)
{
    
    
    if (enable_free_hook)
    {
    
     //调用free
        enable_free_hook = 0;
        __libc_free(p);
        char buff[128] = {
    
    0};
        sprintf(buff, "./mem/%p.mem", p);
        if (unlink(buff) < 0) //删除文件,如果返回小于0,说明释放了2次
        {
    
    
            printf("double free:%p\n", p);
        }
        printf("free:%p\n", p);
        enable_free_hook = 1; //保证下次free,还走if
    }
    else
    {
    
     //其他系统API调用会直接调用__libc_free.
        __libc_free(p);
    }
}

测试代码

	enable_malloc_hook = 1;
    void *p1 = malloc(10);
    void *p2 = malloc(20);
    int d = ccccc + ccccc;
    free(p1);

现有工具介绍

  1. valgrind
  2. mtrace

其中mtrace的使用

	mtrace();
    void *p1 = malloc(10);
    void *p2 = malloc(20); //
    free(p1);
    muntrace();

猜你喜欢

转载自blog.csdn.net/qq_34954047/article/details/122115499