- 方法一:dlsym实现hook
- 方法二:宏定义实现hook
- 方式三:修改__malloc_hook和 __free_hook
- 方式四:mtrace
- 补充:
- 1. __builtin_return_address()使用
c/c++没有垃圾回收机制,因此可能会出现内存泄漏
出现原因:内存分配与内存释放,没有做到匹配
如何知道内存泄漏:
每次malloc/calloc/realloc 就 +1
每次free 就 -1
如果正常退出,不为0,说明存在内存泄漏
如何定位哪一行引起内存泄漏
1.c自带的宏,__FILE__
、_ FUNCTION _
和、__LINE__
,可以定位到具体文件,函数,哪一行
2.builtin_return_address()
,返回函数在哪个地方调用的代码段地址(后续通过addr2line命令解析出现的位置)
如何去实现
通过hook的方法,可以在不修改原来代码的情况下,进行检测。
如何记录呢,每次malloc都创建一个文件,每次free就删除文件,运行完后,看有多少文件就行了。
extern void *__libc_malloc(size_t size);
int enable_malloc_hook = 1;
extern void __libc_free(void* p);
int enable_free_hook = 1;
// func --> malloc() { __builtin_return_address(0)}
// callback --> func --> malloc() { __builtin_return_address(1)}
// main --> callback --> func --> malloc() { __builtin_return_address(2)}
//calloc, realloc
void *malloc(size_t size) {
if (enable_malloc_hook) {
enable_malloc_hook = 0;
void *p = __libc_malloc(size);
void *caller = __builtin_return_address(1); // 1
char buff[128] = {0};//用于存放文件名
sprintf(buff, "./mem/%p.mem", p);
FILE *fp = fopen(buff, "w");
fprintf(fp, "[+%p] --> addr:%p, size:%ld\n", caller, p, size);//将调用malloc的位置信息 写入到 文件流中
fflush(fp);//强迫将缓冲区内的数据写回参数stream指定的文件中
//fclose(fp); //free
enable_malloc_hook = 1;
return p;
} else {
return __libc_malloc(size);
}
return NULL;
}
void free(void *p) {
if (enable_free_hook) {
enable_free_hook = 0;
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
if (unlink(buff) < 0) { // no exist 删除该文件,如果小于0就代表不存在
printf("double free: %p\n", p);
}
__libc_free(p);
// rm -rf p.mem
enable_free_hook = 1;
} else {
__libc_free(p);
}
}
编译前要加上-g
因此可以通过查看目录下有没有 ./mem/xxxxxxx.mem的文件,有的话就说明内存泄漏了。
void *caller = __builtin_return_address(1);
的返回值caller就是 代码段的地址(文章最后有介绍参数)。
然后通过以下命令(addr2line)(-e表示execute -a表示代码段地址)可以查看 内存泄漏的行数
addr2line -f -e memleak -a 0x4009f7
注意点1:
自己实现一个malloc,里面执行printf,发现一直在malloc里面循环,因为printf本身内部就实现了一个malloc,这样就会陷入一个循环
size=10是我们调用的,1024是printf内部调用的
malloc内部调用__libc_malloc这个函数
因此通过enable_malloc_hook来标识,是否使用自己定义的malloc,在printf的前enable_malloc_hook置为0,printf后enable_malloc_hook=1,来避免这个问题
注意点2:
这边不能添加fclose(); 因为fclose()内部就调用了free
如果执行fclose(fp)
这样导致会free
(fclose内部调用了free)掉一个系统指针,而这个指针本身就是不存在的,因此会报double free的错误。
通过宏定义的方式,实现hook。
实现的方式是一样的,还是通过文件保存与删除的方式来记录是否有内存泄漏,只是换成了__FILE__
, __LINE__
(系统提供的宏定义),获取当前的文件和行数。
void *malloc_hook(size_t size, const char *file, int line) {
void *p = malloc(size);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
FILE *fp = fopen(buff, "w");
fprintf(fp, "[+%s:%d] --> addr:%p, size:%ld\n", file, line, p, size);
fflush(fp);
fclose(fp);//这种方法里面是可以用fclose的
return p;
}
void free_hook(void *p, const char *file, int line) {
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);
}
//宏定义,这样子就很方便,想要调试,想知道内存泄漏的时候,可以开启debug,不测试可以关掉
//不能放在前面,因为printf会调用malloc,会导致陷入死循环
#if 1
#define malloc(size) malloc_hook(size, __FILE__, __LINE__)
#define free(p) free_hook(p, __FILE__, __LINE__)
#endif
方式三:修改__malloc_hook和 __free_hook
其中caller就是代码段的地址,通过addr2line命令可以获取 出现内存泄漏的行号。
typedef void *(*malloc_hook_t)(size_t size, const void *caller);
malloc_hook_t malloc_f;
typedef void (*free_hook_t)(void *p, const void *caller);
free_hook_t free_f;
int replaced = 0;//为0代表指向系统自带的,如果为1就代表指向自己实现的hook
void mem_trace(void);
void mem_untrace(void);
void *malloc_hook_f(size_t size, const void *caller) {
mem_untrace();//解除hook,不然下面会继续执行自己的malloc_hook会导致死循环
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);//caller就是代码段的地址 用addr2line命令查看行数
fflush(fp);
fclose(fp); //free
mem_trace();//开启hook
return ptr;
}
void *free_hook_f(void *p, const void *caller) {
mem_untrace();
//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存储起来
free_f = __free_hook;//将 系统调用的free_hook存储起来
__malloc_hook = malloc_hook_f;//用自己实现的malloc_hook 取代
__free_hook = free_hook_f;//用自己实现的free_hook 取代
}
void mem_untrace(void) {
__malloc_hook = malloc_f;//恢复
__free_hook = free_f;
replaced = 0;
}
测试
mem_trace();
void *p1 = malloc(10);
void *p2 = malloc(20); //calloc, realloc
free(p1);
void *p3 = malloc(20);
void *p4 = malloc(20);
free(p2);
free(p4);
mem_untrace();
方式四:mtrace
使用mtrace进行内存泄漏检测
补充: 1. __builtin_return_address()使用返回的是函数代码段的地址
__builtin_return_address()中的参数,0代表调用的地方,1代表调用的上一级栈,2代表调用的上两级栈
func--->maloc() {__builtin_return_address(0)}
main-->func--->maloc() {__builtin_return_address(1)}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)