Linux C/C++服务器
内存泄漏检测组件
调用malloc或者new,没有调用free或delete
内存泄漏检测组件
- 如何判断是否有内存泄漏
- 到底在哪行代码泄漏
本文介绍三种内存泄漏检测的方法
- mtrace
- 宏定义
- hook malloc
mtrace
mtrace是系统自带的内存检测接口,引入mcheck.h头文件就可使用,使用也比较简单
#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h>
int main() {
mtrace();
void *p1 = malloc(10);
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
muntrace();
}
执行过程
gcc -o memleak memleak.c -g
export MALLOC_TRACE=./mem.txt
./memleak
cat mem.txt
addr2line -f -e memleak -a 0x400ad0
低版本可行,在高版本中不会生成mem.txt文件
宏定义
只适合在单文件内,把它放在文件的最前面
为什么main函数中的malloc调用的是宏定义,而void _malloc()内调用的却不是宏定义?
预编译:1.把代码的注释取消 2.宏定义展开,把宏定义直接搬过来,所以在打印行号的时候不会出问题
编译:把各个字符转代码文件换成各个二进制文件
链接:把各个文件的代码模块组装起来
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void *_malloc(size_t size, const char *filename, 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", filename, line, p, size);
fflush(fp);
fclose(fp);
//printf("[+]%s:%d, %p\n", filename, line, p);
return p;
}
void _free(void *p, const char *filename, int line) {
//printf("[-]%s:%d, %p\n", filename, line, p);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
if (unlink(buff) < 0) { //删除文件
printf("double free: %p\n", p);
return ;
}
return free(p);
}
#define malloc(size) _malloc(size, __FILE__, __LINE__)
#define free(size) _free(size, __FILE__, __LINE__)
int main() {
void *p1 = malloc(10); //_malloc(size, __FILE__, __LINE__)
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
return 0;
}
执行过程
gcc -o memleak memleak.c -g
mkdir mem
./memleak
hook
hook接口在调试追踪代码中使用的较多,主要是通过dlsym()和typedef函数指针类型来实现,在之前死锁检测组件中我们也有使用和详细说明
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
typedef void *(*malloc_t)(size_t size);
typedef void (*free_t)(void *ptr);
malloc_t malloc_f = NULL;
free_t free_f = NULL;
int enable_malloc_hook = 1;
void *malloc(size_t size) {
void *p = NULL;
if(enable_malloc_hook){
enable_malloc_hook = 0;
//printf内部也会调用malloc,如果不加enable_malloc_hook标志位终止条件,将导致递归出现段错误
//printf("size: %ld\n", size);
p = malloc(size);
void *caller = __builtin_return_address(0); //可返回是哪里调用的本函数
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);
fflush(fp);
fclose(fp);
enable_malloc_hook = 1;
}else{
p = malloc(size);
}
return p;
}
void free(void *ptr) {
if(enable_malloc_hook){
enable_malloc_hook = 0;
//printf("ptr: %p\n", ptr);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", ptr);
if (unlink(buff) < 0) {
printf("double free: %p\n", ptr);
return ;
}
free_f(ptr);
enable_malloc_hook = 1;
}
}
static void init_hook(void) {
if (malloc_f == NULL) {
malloc_f = (malloc_t)dlsym(RTLD_NEXT, "malloc");
}
if (free_f == NULL) {
free_f = (free_t)dlsym(RTLD_NEXT, "free");
}
}
int main() {
init_hook(); //3.hook
void *p1 = malloc(10);
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
return 0;
}
执行过程
gcc -o memleak memleak.c -g -ldl
mkdir mem
./memleak
cat mem/0x55818bb172a0.mem
addr2line -f -e memleak -a 0x1651
以上只是给出了检测内存泄漏的方法,但实际工程中检测内存泄漏要复杂的多,而且并不能100%的检测出来