微信公众号:二进制人生
专注于嵌入式linux开发。问题或建议,请发邮件至hjhvictory@163.com。
更新日期:2020/04/13,内容整理自网络,转载请注明出处。
通过在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 <stdio.h>
#include <malloc.h>
//加锁解锁暂时省了
#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会打印调试信息,调试信息包括分配位置文件名、函数、行号、分配大小、地址,方便定位问题。
图:二进制人生公众号





