malloc提供的回调注册机制--定位内存泄漏

 

微信公众号:二进制人生
专注于嵌入式linux开发。问题或建议,请发邮件至[email protected]
更新日期:2020/04/13,内容整理自网络,转载请注明出处。

malloc提供的回调注册机制--定位内存泄漏_第1张图片

图:二进制人生公众号

 

通过在malloc、free之前或者之后打印调试信息或者其他统计操作,可以帮助定位内存泄露问题。我们通常通过修改malloc来实现这一目的。
网上写了很多种修改malloc的方法:
1、自行二次封装,如:

static int malloc_check = 0;
void* MemMalloc(int ilen)
{
    if(0 == malloc_check )
    {
        return malloc(ilen);
    }
    else
    {
        void *addr = malloc(ilen);
    //打印信息,或者统计操作
    return addr;
    }
}

2、
通过malloc提供的钩子来实现可以达到全局有效的效果,比方说第三方库用了malloc,也会被我们修改,而那种二次封装的方法做不到。
二次封装的做法必须调用MemMalloc,然而第三方库使用的还是malloc,因此无法定位第三方库的问题。钩子方法缺陷就是为保证线程安全,需要自行加锁。
还有很多修改malloc的方法,我们今天重点讲钩子的方法。

malloc提供了一组钩子,让用户有途径修改malloc/free/remalloc等接口的实现。所谓的钩子就是回调函数指针。

/* Hooks for debugging and user-defined versions. */
extern void(*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr,
                          const void *)
__MALLOC_DEPRECATED;
extern void *(*__MALLOC_HOOK_VOLATILE __malloc_hook)(size_t __size,
                             const void *)
__MALLOC_DEPRECATED;
extern void *(*__MALLOC_HOOK_VOLATILE __realloc_hook)(void *__ptr,
                              size_t __size,
                              const void *)
__MALLOC_DEPRECATED;
extern void *(*__MALLOC_HOOK_VOLATILE __memalign_hook)(size_t __alignment,
                               size_t __size,
                               const void *)
__MALLOC_DEPRECATED;
extern void(*__MALLOC_HOOK_VOLATILE __after_morecore_hook) (void);

看下malloc函数的开头就知道__malloc_hook钩子的作用了。

void *
__libc_malloc(size_t bytes)
{
        ...//变量定义和assert参数检查
    void *(*hook) (size_t, const void *)
        = atomic_forced_read(__malloc_hook);//钩子是全局函数指针,考虑到多线程多进程,访问时要加锁
    if (__builtin_expect(hook != NULL, 0))//如果钩子非空,则调用钩子并返回
    {
        return (*hook)(bytes, RETURN_ADDRESS(0));
    }

__free_hook钩子的作用也是一样的,不贴代码了。

通常不会有人闲的蛋疼,自己去实现整个malloc。但是我们有这种需求:想在malloc里加点打印信息。我们可以把__malloc__hook钩子修改成自己的函数,这样子调用malloc时就会调用我们修改后的函数。然而我们没有能力去实现整个malloc,我们在做完自己的事后,重新调用原来的malloc。在重新调用原来的malloc之前,需要把钩子还原回去,不然等下malloc又调用了我们修改的函数。等原来的malloc执行完毕,我们还得把钩子改成我们修改的函数,这样下次malloc时又会进到我们修改后的函数。

我们来演示下通过malloc提供的__malloc_hook、__free_hook钩子在malloc、free加点打印信息。

#include 
#include 

//加锁解锁暂时省了
#define THREAD_LOCK
#define    THREAD_UNLOCK

static void (*old_free)(void *ptr, const void *caller);
static void *(*old_malloc)(size_t size, const void *caller);

static void my_free(void *ptr, const void *caller);
static void *my_malloc(size_t size, const void *caller);

static void hook_back()
{
    old_malloc = __malloc_hook;
    old_free = __free_hook;
}

static void hook_init()
{
    __malloc_hook = my_malloc;
    __free_hook = my_free;
}

static void hook_restore()
{
    __malloc_hook = old_malloc;
    __free_hook = old_free;
}

static void my_free(void *ptr, const void *caller)
{
    THREAD_LOCK;//要加锁,考虑多线程的话
    hook_restore();//这里必须,不然会死循环
    printf("free address: %x\n", ptr);
    free(ptr);
    hook_init();//重新初始化钩子
    THREAD_UNLOCK;
    return;
}

static void *my_malloc(size_t size, const void *caller)
{
    THREAD_LOCK;//要加锁,考虑多线程的话
    void *p = NULL;
    hook_restore();//这里必须,不然会死循环
    p = malloc(size);
    printf("%s-%s-%d:malloc start=%p,size=%d,\n", __FILE__,__FUNCTION__,__LINE__,p,size);//打印文件名,函数,行号,方便定位问题
    hook_init();//重新初始化钩子
    THREAD_UNLOCK;
    return p;
}

void *(*__MALLOC_HOOK_VOLATILE __malloc_hook) (size_t size, const void *caller) = my_malloc;

int main(int argc, char **argv)
{
    void *p = malloc(16);
    if (p != NULL)
        free(p);

    hook_restore();//恢复正常

    p = malloc(24);
    if (p != NULL)
        free(p);
    return 0;
}

运行结果

root@AI-Machine:/home/hongjh/2019_project# ./a.out 
malloc_hook.c-my_malloc-54:malloc start=0xb7c00470,size=16,
free address: 

只有第一个malloc会打印调试信息,调试信息包括分配位置文件名、函数、行号、分配大小、地址,方便定位问题。

你可能感兴趣的:(专辑8,---,嵌入式linux,C基础)