<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
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

    文章

    8196

    瀏覽量

    82751
  • Free
    +關注

    關注

    0

    文章

    16

    瀏覽量

    10963
  • 內存泄漏
    +關注

    關注

    0

    文章

    38

    瀏覽量

    9171
  • 虛擬內存
    +關注

    關注

    0

    文章

    70

    瀏覽量

    8024
收藏 人收藏

    評論

    相關推薦

    煤氣泄漏檢測系統!畢業設計

    煤氣泄漏檢測系統!畢業設計,高手請幫忙!
    發表于 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 ?1762次閱讀
    <b class='flag-5'>泄漏檢測</b>及定位原理

    沼氣泄漏檢測電路

    沼氣泄漏檢測電路
    發表于 02-15 13:35 ?453次閱讀
    沼氣<b class='flag-5'>泄漏檢測</b>電路

    沼氣泄漏檢測電路

    沼氣泄漏檢測電路
    發表于 07-05 11:39 ?673次閱讀
    沼氣<b class='flag-5'>泄漏檢測</b>電路

    C++內存泄漏檢測拾遺

    在MFC開發環境中,當運行退出了,Visual Studio會在輸出窗口提示是否有內存泄漏。也可以借助MFC類CMemoryState動態地檢測并輸出內存
    發表于 05-27 09:59 ?900次閱讀

    騰訊內部內存泄漏分析工具簡析

    精益求精。 鏈接: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 ?2588次閱讀

    真空泄漏檢測儀的重要性和應用

    真空泄漏檢測儀是一種強大的設備,它能夠檢測和定位系統或部件的微小泄漏。在許多行業中,包括汽車、航空航天、醫療設備和半導體等,這種設備都是必不可少的。下面我們將詳細討論真空泄漏檢測儀的重
    的頭像 發表于 08-15 09:52 ?561次閱讀

    基于C++代碼實現內存泄漏檢測工具

    看到的一個文章,有人用一個很簡短的代碼實現了內存檢測工具,大家看看實用性如何?
    發表于 08-21 10:11 ?324次閱讀
    基于C++代碼實現<b class='flag-5'>內存</b><b class='flag-5'>泄漏檢測工具</b>

    Linux內存泄漏該如何去檢測呢?

    mtrace(memory trace),是 GNU Glibc 自帶的內存問題檢測工具,它可以用來協助定位內存泄露問題。
    的頭像 發表于 09-21 09:37 ?700次閱讀
    Linux<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>該如何去<b class='flag-5'>檢測</b>呢?
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>