0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何写一个内存泄漏检测工具

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-11 16:19 次阅读

如何确定有内存泄露问题,如何定位到内存泄露位置,如何写一个内存泄漏检测工具?

1:概述

内存泄露本质:其实就是申请调用malloc/new,但是释放调用free/delete有遗漏,或者重复释放的问题。

内存泄露会导致的现象:作为一个服务器,长时间运行,内存泄露会导致进程虚拟内存被占用完,导致进程崩溃吧。(堆上分配的内存)

如何规避或者发现内存泄露呢?

===》1:如何检测有内存泄露?(除了内存监控工具htop,耗时,效果不明显)

===》2:如何定位内存泄露的代码问题(少量代码可以阅读代码排除,线上版本呢?)

=====》引入gc

=====》少量代码可以通过排查代码进行定位

=====》已经确定代码有内存泄露,可以用过valgrind/mtrace等市场上已有的一些工具

=====》本质是malloc和free的次数不一致导致,我们通过hook的方式,对malloc和free次数进行统计

2:通过hook的方式检测,定位内存泄露(四种方法)

在生产环境重定位内存泄露的问题,我们可以在产品中增加这些定位手段,通过配置文件开关控制其打开,方便内存泄露定位。

几种不同的方式本质:都是对malloc和free进行hook,增加一些处理进行检测。

2.1:测试代码描述内存泄露

如下代码,从代码看,明显可以看到是有内存泄露的,但是如果看不到代码,或者代码量过多,从运行现象上我们就很难发现了。

#include < stdio.h >
#include < stdlib.h >

int main()
{
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

//代码运行是没有问题,也没有报错的,但是明显可以看到ptr2是没有内存释放的,如果是服务器有这种代码,长时间运行会有严重问题的。

2.2:通过dlsym库函数对malloc/free进行hook

我在 Linux/unix系统编程手册 这本书中了解相关dlsym函数的使用

要想知道有内存泄露,或者直接定位内存泄露的代码位置,本质还是对调用的malloc/free进行hook, 对调用malloc/free分别增加监控来分析。

使用dlsym库函数,获取malloc/free函数的地址,通过RTLD_NEXT进行比标记(这个标记适用于在其他地方定义的函数同名的包装函数,如在主程序中定义的malloc,代替系统的malloc),实现用我们主程序中malloc代替系统调用malloc.

2.2.1:第一版试着

在调用malloc和free前,使用dlsym函数和RTLD_NEXT标记,获取系统库malloc/free地址,以及用本地定义的malloc/free代替系统调用。

//1:使用void * dlsym(void* handle, char* symbool)函数和handle为RTLD_NEXT标记,对malloc/free进行hook
//2:RTLD_NEXT标记 需要在本地实现 symbool同名函数达到hook功能,即这里要实现malloc/free功能
//3:dlsym()返回的是symbool 参数对应的函数的地址,在同名函数中用该地址实现真正的调用

//RTLD_NEXT 是dlsym() 库中的伪句柄,定义_GNU_SOURCE宏才能识别
//可以通过 man dlsym
//测试发现 :必须放在最顶部,不然编译报 RTLD_NEXT没有定义 
#define _GNU_SOURCE
#include < dlfcn.h >  //对应的头文件

//第一步功能,确定hook成功,先在我们的hook函数中增加一些打印信息验证
#include < stdio.h >
#include < stdlib.h >

//定义相关全局变量,获取返回的函数地址,进行实际调用
typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;

typedef void (*free_t)(void* p);
free_t free_f = NULL;

//要hook的同名函数
void * malloc(size_t size){
    printf("exec malloc n");
    return malloc_f(size);
}

void free(void * p){
    printf("exec free n");
    free_f(p);
}
//通过dlsym 对malloc和free使用前进行hook
static void init_malloc_free_hook(){
    //只需要执行一次
    if(malloc_f == NULL){
        malloc_f = dlsym(RTLD_NEXT, "malloc"); //除了RTLD_NEXT 还有一个参数RTLD_DEFAULT
    }

    if(free_f == NULL)
    {
        free_f =  dlsym(RTLD_NEXT, "free");
    }
    return ;
}


int main()
{
    init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

上述代码是有问题的,现象及定位问题:

hlp@ubuntu:~/mem_test$ gcc dlsym_hook.c -o dlsym_hook -ldl
hlp@ubuntu:~/mem_test$ ./dlsym_hook 
Segmentation fault (core dumped)


#使用gdb对问题进行定位 
hlp@ubuntu:~/mem_test$ gdb ./dlsym_hook 
(gdb) b 54    #加断点
Breakpoint 1 at 0x400729: file dlsym_hook.c, line 54.
(gdb) b 28	  #加断点
Breakpoint 2 at 0x400682: file dlsym_hook.c, line 28.
(gdb) r       #开始运行
Starting program: /home/hlp/mem_test/dlsym_hook 
Breakpoint 1, main () at dlsym_hook.c:54
54	    void * ptr1 = malloc(10);
(gdb) c		#单步执行
Continuing.
Breakpoint 2, malloc (size=10) at dlsym_hook.c:28   #第一个mallocy已经执行
28	    printf("exec malloc n");	
(gdb) c
Continuing.
Breakpoint 2, malloc (size=1024) at dlsym_hook.c:28  #这里的1024不是我们代码里面的,
28	    printf("exec malloc n");
(gdb) c
Continuing.

Breakpoint 2, malloc (size=1024) at dlsym_hook.c:28    #发现malloc 1024一直循环执行 怀疑是printf中会调用malloc,
28	    printf("exec malloc n");
(gdb) c
Continuing.

Breakpoint 2, malloc (size=1024) at dlsym_hook.c:28
28	    printf("exec malloc n");
(gdb) 

#通过gdb进行定位时,可以确定,我们hook malloc函数内部调用printf,printf底层其实是有调用malloc,从而printf内部成为递归,一直调用了。
#所以我们需要规避这种现象,让hook函数内部其他业务只执行一次,不要因为第三方库内部机制导致类似问题

增加特定标识,优化上述代码:

//使用标识,使hook的函数内部只执行一次,不因为第三方库原因导致递归现象
#define _GNU_SOURCE
#include < dlfcn.h >  //对应的头文件
#include < stdio.h >
#include < stdlib.h >

typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;
typedef void (*free_t)(void* p);
free_t free_f = NULL;

//定义一个hook函数的标志 使内部逻辑只执行一次
int enable_malloc_hook = 1;
int enable_free_hook = 1;

//要hook的同名函数
void * malloc(size_t size){
    if(enable_malloc_hook) //对第三方调用导致的递归进行规避
    {
        enable_malloc_hook = 0;
        printf("exec malloc n");
        enable_malloc_hook = 1;
    }
    return malloc_f(size);
}

void free(void * p){
    if(enable_free_hook){
        enable_free_hook = 0;
        printf("exec free n");
        enable_free_hook = 1;
    }
    free_f(p);
}

//通过dlsym 对malloc和free使用前进行hook
static void init_malloc_free_hook(){
    //只需要执行一次
    if(malloc_f == NULL){
        malloc_f = dlsym(RTLD_NEXT, "malloc"); //除了RTLD_NEXT 还有一个参数RTLD_DEFAULT
    }

    if(free_f == NULL)
    {
        free_f =  dlsym(RTLD_NEXT, "free");
    }
    return ;
}
int main()
{
    init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

上述代码执行成功,现象如下:

hlp@ubuntu:~/mem_test$ gcc dlsym_hook_ok.c -o dlsym_hook_ok -ldl -g
hlp@ubuntu:~/mem_test$ ./dlsym_hook_ok 
exec malloc 
exec malloc 
exec free 
exec malloc 
exec free
#对比执行的 malloc和free次数 可以确定有内存泄露

如何增加行号标识呢?让我们确定到代码位置?

如何确定有内存泄露呢?直接通过代码,识别到malloc/free的对应次数,定位到有代码问题的位置。

2.2.2:能识别到行号,以及有问题代码位置

//我们知道,一般可以通过__LINE__ 标识日志当前行号位置,但是这里不适用
//可以通过 __builtin_return_address 获取上级调用的退出的地址,可以设置时1级,也可以设置是2级别...

// 增加打印调用malloc和free位置的信息。 这里打印地址  通过addr2line进行地址和行号转换
#define _GNU_SOURCE
#include < dlfcn.h >  //对应的头文件
#include < stdio.h >
#include < stdlib.h >

typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;
typedef void (*free_t)(void* p);
free_t free_f = NULL;

int enable_malloc_hook = 1;
int enable_free_hook = 1;

void * malloc(size_t size){
    if(enable_malloc_hook) //对第三方调用导致的递归进行规避
    {
        enable_malloc_hook = 0;
        //打印上层调用的地址

        void *carrer = __builtin_return_address(0);
        printf("exec malloc [%p ]n", carrer );
        enable_malloc_hook = 1;
    }
    return malloc_f(size);
}

void free(void * p){
    if(enable_free_hook){
        enable_free_hook = 0;
        void *carrer = __builtin_return_address(0);
        printf("exec free [%p]n", carrer);
        enable_free_hook = 1;
    }
    free_f(p);
}

//通过dlsym 对malloc和free使用前进行hook
static void init_malloc_free_hook(){
    //只需要执行一次
    if(malloc_f == NULL){
        malloc_f = dlsym(RTLD_NEXT, "malloc"); //除了RTLD_NEXT 还有一个参数RTLD_DEFAULT
    }

    if(free_f == NULL)
    {
        free_f =  dlsym(RTLD_NEXT, "free");
    }
    return ;
}
int main()
{
    init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

执行结果及查找对应行数:

#执行结果如下
hlp@ubuntu:~/mem_test$ gcc dlsym_hook_addr.c -o dlsym_hook_addr -ldl
hlp@ubuntu:~/mem_test$ ./dlsym_hook_addr 
exec malloc [0x400797 ]
exec malloc [0x4007a5 ]
exec free [0x4007b5]
exec malloc [0x4007bf ]
exec free [0x4007cf]
#可以通过addr2line 获取到对应的代码行数 编译的时候要带 -g
hlp@ubuntu:~/mem_test$ addr2line -fe ./dlsym_hook_addr -a 0x400797
0x0000000000400797
main
/home/hlp/mem_test/dlsym_hook_addr.c:57

2.2.3:通过策略,查找有问题的代码

从上文可以知道,我们通过对malloc和free的hook,可以获得各自malloc和hook的次数。

以及我们可以通过__builtin_return_address 接口获取到实际调用malloc/free的位置。

除此之外,malloc之间有所关联的是申请内存的地址,

汇总:

===》可以思考,通过malloc和free关联的地址作为标识,对malloc和free的次数进行统计即可。

===》可以设计数据结构,对不同地址,malloc的地址和free的地址进行保存,malloc的次数和free的次数进行控制判断

===》这里根据老师的逻辑,用文件的方式进行控制。

测试代码如下:

// malloc和free 之间的关联是申请内存的地址,以该地址作为基准
// malloc时写入一个文件,打印行数等必要信息  free时删除这个文件 通过有剩余文件判断内存泄露
#define _GNU_SOURCE
#include < dlfcn.h >  //对应的头文件
#include < stdio.h >
#include < stdlib.h >
#include < unistd.h >

typedef void *(*malloc_t)(size_t size);
malloc_t malloc_f = NULL;
typedef void (*free_t)(void* p);
free_t free_f = NULL;

int enable_malloc_hook = 1;
int enable_free_hook = 1;

#define MEM_FILE_LENGTH 40
void * malloc(size_t size){
    if(enable_malloc_hook) //对第三方调用导致的递归进行规避
    {
        enable_malloc_hook = 0;
        //实际的内存申请,根据该地址写文件和free 相互关联
        void *ptr =malloc_f(size);
        //打印上层调用的地址
        void *carrer = __builtin_return_address(0);
        printf("exec malloc [%p ]n", carrer );

        //通过写入文件的方式 对malloc和free进行关联  malloc时写入文件
        char file_buff[MEM_FILE_LENGTH] = {0};
        sprintf(file_buff, "./mem/%p.mem", ptr);

        //打开文件写入必要信息 使用前创建目录级别
        FILE *fp = fopen(file_buff, "w");
        fprintf(fp, "[malloc addr : +%p ] ---- >mem:%p  size:%lu n",carrer, ptr, size);
        fflush(fp); //刷新写入文件

        enable_malloc_hook = 1;
        return ptr;
    }else
    {
         return malloc_f(size);
    }
}

void free(void * p){
    if(enable_free_hook){
        enable_free_hook = 0;
        void *carrer = __builtin_return_address(0);

        //free时删除文件  根据剩余文件判断内存泄露
        char file_buff[MEM_FILE_LENGTH] = {0};
        sprintf(file_buff, "./mem/%p.mem", p);
        //删除文件 根据malloc对应的指针
        if(unlink(file_buff) < 0)
        {
            printf("double free: %p, %p n", p, carrer);
        }
        //这里的打印实际就没意义了
        printf("exec free [%p]n", carrer);
        free_f(p);
        enable_free_hook = 1;
    }else
    {
        free_f(p);
    }
}

//通过dlsym 对malloc和free使用前进行hook
static void init_malloc_free_hook(){
    //只需要执行一次
    if(malloc_f == NULL){
        malloc_f = dlsym(RTLD_NEXT, "malloc"); //除了RTLD_NEXT 还有一个参数RTLD_DEFAULT
    }

    if(free_f == NULL)
    {
        free_f =  dlsym(RTLD_NEXT, "free");
    }
    return ;
}
int main()
{
    init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

执行结果:

# 这里的打印只是为了理解  没有多大意义,真正的分析还得依靠文件目录
hlp@ubuntu:~/mem_test$ mkdir mem
hlp@ubuntu:~/mem_test$ gcc dlsym_hook_file.c -o dlsym_hook_file -ldl -g
hlp@ubuntu:~/mem_test$ ./dlsym_hook_file 
exec malloc [0x400ad5 ]
exec malloc [0x400ae3 ]
exec free [0x400af3]
exec malloc [0x400afd ]
exec free [0x400b0d]
hlp@ubuntu:~/mem_test$ cd mem/

# 这里在我们的目标目录下  看到有文件存在,说明存在内存泄露
hlp@ubuntu:~/mem_test/mem$ ls
0xe38680.mem
# 通过文件中的日志信息,对其进行分析,找到问题代码位置
hlp@ubuntu:~/mem_test/mem$ cat 0xe38680.mem 
[malloc addr : +0x400ae3 ] ---- >mem:0xe38680  size:20 
hlp@ubuntu:~/mem_test/mem$ cd ../
#通过地址转换 找到我们有问题代码 没有释放的代码位置 
hlp@ubuntu:~/mem_test$ addr2line -fe ./dlsym_hook_file 0x400ae3
main
/home/hlp/mem_test/dlsym_hook_file.c:85

2.3:通过宏定义的方式对malloc/free进行hook

本质其实就是对系统调用的malloc/free进行替换,调用我们的目标方法,可以通过hook或者重载的方法实现。

使用宏定义的方式,实现malloc/free的替换。

#include < stdio.h >
#include < stdlib.h >

//不能放在这里  放在这里  会对malloc_hook 和 free_hook 内部实际调用的也替换,就形成的递归调用了 并且无法规避
//#define malloc(size) 	malloc_hook(size, __FILE__, __LINE__)
//#define free(p) 		free_hook(p,  __FILE__, __LINE__)

#define MEM_FILE_LENGTH 40
//实现目标函数
void *malloc_hook(size_t size, const char* file, int line)
{
    //这里还是通过文件的方式进行识别
    void *ptr =malloc(size);
    char file_name_buff[MEM_FILE_LENGTH] = {0};
    sprintf(file_name_buff, "./mem/%p.mem", ptr);

    //打开文件写入必要信息 使用前创建目录级别
    FILE *fp = fopen(file_name_buff, "w");
    fprintf(fp, "[file:%s  line:%d ] ---- >mem:%p  size:%lu n",file, line, ptr, size);
    fflush(fp); //刷新写入文件

    printf("exec malloc [%p:%lu], file: %s, line:%d n", ptr, size, file, line );
    return ptr;
}

void free_hook(void *p, const char* file, int line)
{
    char file_name_buff[MEM_FILE_LENGTH] = {0};
    sprintf(file_name_buff, "./mem/%p.mem", p);

    if(unlink(file_name_buff) < 0)
    {
        printf("double free: %p, file: %s. line :%d n", p, file, line);
    }

    //这里的打印实际就没意义了
    printf("exec free [%p], file: %s line:%d n", p, file, line);
    free(p);
}
//宏定义实现代码中调用malloc/free时调用我们目标函数
#define malloc(size) 	malloc_hook(size, __FILE__, __LINE__)
#define free(p) 		free_hook(p,  __FILE__, __LINE__)

int main()
{
    //init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

代码执行如下:

hlp@ubuntu:~/mem_test$ gcc define_hook.c -o define_hook
hlp@ubuntu:~/mem_test$ ./define_hook 
exec malloc [0x1f91010:10], file: define_hook.c, line:47 
exec malloc [0x1f92680:20], file: define_hook.c, line:48 
exec free [0x1f91010], file: define_hook.c line:50 
exec malloc [0x1f938e0:30], file: define_hook.c, line:52 
exec free [0x1f938e0], file: define_hook.c line:53 
#通过目录下文件 以及文件内容  可以分析到代码有问题的行号
#注意  测试前最好清空目录下的文件 
hlp@ubuntu:~/mem_test$ ls ./mem
0x1f92680.mem
hlp@ubuntu:~/mem_test$ cat ./mem/0x1f92680.mem 
[file:define_hook.c  line:48 ] ---- >mem:0x1f92680  size:20

2.4:_libc_malloc

对malloc进行劫持 使用实际的内存申请_libc_malloc 进行申请内存以及其他控制

#include < stdio.h >
#include < stdlib.h >
#include < unistd.h >
//实际内存申请的函数
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(0); // 0
		
		char buff[128] = {0};
		sprintf(buff, "./mem/%p.mem", p);

		FILE *fp = fopen(buff, "w");
		fprintf(fp, "[+%p] -- > addr:%p, size:%ldn", caller, p, size);
		fflush(fp);

		//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
			printf("double free: %pn", p);
		}
		
		__libc_free(p);

		// rm -rf p.mem
		enable_free_hook = 1;

	} else {
		__libc_free(p);
	}
}

int main()
{
    //init_malloc_free_hook(); //执行一次
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    return 0;
}

代码运行及分析如下:

hlp@ubuntu:~/mem_test$ gcc libc_hook.c -o libc_hook -g
hlp@ubuntu:~/mem_test$ ./libc_hook 
#通过查看目录下生成的文件   可以知道有内存泄露 通过日志内部信息  使用addr2line 通过打印地址找到对应的代码位置
hlp@ubuntu:~/mem_test$ cat ./mem/0x733270.mem 
[+0x4009f7] -- > addr:0x733270, size:20
hlp@ubuntu:~/mem_test$ addr2line -fe ./libc_hook 0x4009f7
main
/home/hlp/mem_test/libc_hook.c:69   #找到问题代码位置

2.5:mem_trace

原理:glic提供__malloc_hook, __realloc_hook, __free_hook可以实现hook自定义malloc/free函数

#include < stdlib.h >
#include < stdio.h >
#include < unistd.h >
#include < malloc.h >

	  /* #include < malloc.h >
       void *(*__malloc_hook)(size_t size, const void *caller);
       void *(*__realloc_hook)(void *ptr, size_t size, const void *caller);
       void *(*__memalign_hook)(size_t alignment, size_t size,
                                const void *caller);
       void (*__free_hook)(void *ptr, const void *caller);
       void (*__malloc_initialize_hook)(void);
       void (*__after_morecore_hook)(void);*/
//#include < mcheck.h >

typedef void *(*malloc_hook_t)(size_t size, const void *caller);
typedef void (*free_hook_t)(void *p, const void *caller);

malloc_hook_t 	old_malloc_f = NULL;
free_hook_t 	old_free_f = NULL;
int replaced = 0;

void mem_trace(void);
void mem_untrace(void);

void *malloc_hook_f(size_t size, const void *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:%ldn", caller, ptr, size);
	fflush(fp);

	fclose(fp); //free
	mem_trace();
	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: %pn", p);
		return NULL;
	}
	free(p);
	mem_trace();
}
//对__malloc_hook 和__free_hook 重赋值 
void mem_trace(void) { //mtrace
	replaced = 1;      
	old_malloc_f = __malloc_hook; //malloc -- > 
	old_free_f = __free_hook;

	__malloc_hook = malloc_hook_f;
	__free_hook = free_hook_f;
}

//还原 __malloc_hook 和__free_hook
void mem_untrace(void) {
	__malloc_hook = old_malloc_f;
	__free_hook = old_free_f;
	replaced = 0;
}

int main()
{
    mem_trace();  //mtrace();    //进行hook劫持
    void * ptr1 = malloc(10);
    void * ptr2 = malloc(20);

    free(ptr1);

    void * ptr3 = malloc(30);
    free(ptr3);
    mem_untrace();  //muntrace(); //取消劫持
    return 0;
}

这里编译的时候有一些警告,但是编译成功了,

除此之外 相关资料可以用 man __malloc_hook 去了解

hlp@ubuntu:~/mem_test$ gcc 3.c -o 3 -g
hlp@ubuntu:~/mem_test$ ./3
hlp@ubuntu:~/mem_test$ cat mem/0x1789030.mem 
[+0x400ab2] -- > addr:0x1789030, size:20
hlp@ubuntu:~/mem_test$ addr2line -fe ./3 0x400ab2
main
#可以确定问题代码位置
/home/hlp/mem_test/3.c:79

3:总结:

内存泄露的本质是,在堆上分配内存,使用malloc/calloc/realloc 以及内存的释放free不匹配导致的。

怎么检测,定位内存泄露?:

===》实际上对内存管理的相关函数进行劫持(hook),增加一些必要信息供我们分析。

===》不同的方案,其实就是劫持(hook的方式不一样)

===》可以使用重载,宏定义,操作系统提供的hook方式(__malloc_hook)等不通的方案

===》在hook的基础上,要进行分析,需要用相关的策略,这里用的多个文件,可以用数据结构管理。

===》除此之外,__builtin_return_address函数可以获取函数调用地址,以及addr2line命令对地址和代码行数进行转换,确定问题代码位置。

===》编译的时候,要加-g,addr2line才可用。

这里只是简单的内存泄露hook的一些方案demo,有关多线程等细节再实际项目中也需要考虑。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    12

    文章

    8116

    浏览量

    82509
  • Free
    +关注

    关注

    0

    文章

    16

    浏览量

    10920
  • 内存泄漏
    +关注

    关注

    0

    文章

    38

    浏览量

    9166
  • 虚拟内存
    +关注

    关注

    0

    文章

    70

    浏览量

    8018
收藏 人收藏

    评论

    相关推荐

    煤气泄漏检测系统!毕业设计

    煤气泄漏检测系统!毕业设计,高手请帮忙!
    发表于 03-24 01:48

    如何去解决电信设备内的泄漏检测

    基于电信设备内液体泄漏检测的光电液位传感器用于昂贵和关键系统的低成本泄漏检测解决方案
    发表于 02-23 06:34

    写了内存泄漏检工具

    嵌入式环境内存泄漏检查比较麻烦,valgrind比较适合于在pc上跑,嵌入式上首先移植就很麻烦,移植完了内存比较小,跑起来也比较费劲。所以手动写了
    发表于 12-17 08:25

    基于物联网的LPG气体泄漏检测

    使用MQ-5传感器、ESP8266和Arduino构建基于物联网的LPG气体泄漏检测器。
    发表于 09-22 06:06

    泄漏检测仪校正与调整

    本文概述了泄漏检测仪的基本结构,针对泄漏检测仪出现“误判”故障,在校正及修理调试时采取了相应措施,恢复了泄漏检测仪正常使用功能。
    发表于 01-14 15:29 13次下载

    泄漏检测技术

    从割草机到咖啡机,任何的流体处理设备都需要进行泄漏检测,从而为其投入市场做论证准备。通常,应用在样机设计阶段的泄漏检测方法也是在大批量生产中用于检测的最好方法
    发表于 01-23 12:04 13次下载

    泄漏检测及定位原理

    泄漏检测及定位原理 当管 道 发 生泄漏时,泄漏点处由于管道内外的压差,流体迅速消失,压力下降。泄漏点两边的流体由于存在压差而
    发表于 01-08 11:48 1744次阅读
    <b class='flag-5'>泄漏检测</b>及定位原理

    沼气泄漏检测电路

    沼气泄漏检测电路
    发表于 02-15 13:35 445次阅读
    沼气<b class='flag-5'>泄漏检测</b>电路

    沼气泄漏检测电路

    沼气泄漏检测电路
    发表于 07-05 11:39 668次阅读
    沼气<b class='flag-5'>泄漏检测</b>电路

    C++内存泄漏检测拾遗

    在MFC开发环境中,当运行退出了,Visual Studio会在输出窗口提示是否有内存泄漏。也可以借助MFC类CMemoryState动态地检测并输出内存
    发表于 05-27 09:59 891次阅读

    腾讯内部内存泄漏分析工具简析

    精益求精。 链接:wetest.qq.com 工具使用入口 【工具简介】 tMemoryMonitor简称TMM,是一款运行时C/C++内存泄漏检测工具。TMM认为在进程退出时,
    发表于 10-11 15:30 0次下载
    腾讯内部<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>分析<b class='flag-5'>工具</b>简析

    氨气泄漏的危害_氨气泄漏检测仪怎么使用_氨气泄漏检测仪的使用方法

    氨气泄漏检测仪 氨气泄漏检测仪测量范围:0-100ppm、0-400ppm,声光报警,高防水防尘设计,具有数据存储功能,声光报警。
    发表于 01-03 09:57 2539次阅读

    真空泄漏检测仪的重要性和应用

    真空泄漏检测仪是一种强大的设备,它能够检测和定位系统或部件的微小泄漏。在许多行业中,包括汽车、航空航天、医疗设备和半导体等,这种设备都是必不可少的。下面我们将详细讨论真空泄漏检测仪的重
    的头像 发表于 08-15 09:52 504次阅读

    基于C++代码实现内存泄漏检测工具

    看到的一个文章,有人用一个很简短的代码实现了内存检测工具,大家看看实用性如何?
    发表于 08-21 10:11 281次阅读
    基于C++代码实现<b class='flag-5'>内存</b><b class='flag-5'>泄漏检测工具</b>

    Linux内存泄漏该如何去检测呢?

    mtrace(memory trace),是 GNU Glibc 自带的内存问题检测工具,它可以用来协助定位内存泄露问题。
    的头像 发表于 09-21 09:37 673次阅读
    Linux<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>该如何去<b class='flag-5'>检测</b>呢?