内存泄漏检测组件的实现

  1. 通过宏定义来包装 mallocfree 函数,以便在每次内存分配和释放时记录相关信息,如文件名和行号。这使得你能够跟踪哪个函数在哪里分配和释放内存。

#define _GNU_SOURCE
#include 

#include 
#include 
#include 
#include 


// 方式一:宏定义

void *_malloc(size_t size, char *filename,int line) {

    void *ptr = malloc(size);
    
    char file[128] = {0};
    sprintf(file ,"./mem/%p.mem" ,ptr);
    FILE *fp = fopen(file ,"w");//w小写

    fprintf(fp, "[+]addr: %p filename: %s line: %d\n",ptr,filename,line);

    fflush(fp);
    fclose(fp);

    return ptr;
}

void _free(void *ptr, char *filename,int line) {

    char file[128] = {0};
    sprintf(file,"./mem/%p.mem",ptr);

    if(unlink(file) < 0){//unlink用于在文件系统中删除指定的文件
        printf("double free %p\n",ptr);
        return;
    }

    return free(ptr);

}

// __FILE__ 获取文件名
// __LINE__ 获取函数执行的行号

#define malloc(size)   _malloc(size, __FILE__,__LINE__)
#define free(ptr)      _free(ptr, __FILE__,__LINE__)

int main(void) {

    init_hook();

    void *p1 = malloc(8);
    void *p2 = malloc(16);
    void *p3 = malloc(32);

    free(p1);
    free(p2);

    return 0;
}

编译执行: 

 表示在memleak.c 文件中, 第149行出现内存泄漏问题。

2.通过使用hook方法来重定向 mallocfree 函数, 与此同时通过__builtin_return_address()函数的返回值结合(*caller) 命令: addr2line -f -e ./程序名 -a 返回值(caller) 查看内存泄露的程序及具体行数。

#define _GNU_SOURCE
#include 

#include 
#include 
#include 
#include 

//方式二:hook

// gcc -o memleak memleak.c -g -ldl
// addr2line -f -e ./memleak -a 0x400b38(返回值)

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

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


int enable_malloc_hook = 1;
int enable_free_hook = 1;

void *COnvertToELF(void *addr) {
	Dl_info info;
	struct link_map *link;

	dladdr1(addr, &info, (void**)&link,RTLD_DL_LINKMAP);

	return (void*)((size_t)addr - link->l_addr);
}

void *malloc(size_t size) {

	void *ptr = NULL;
	if (enable_malloc_hook) {
		enable_malloc_hook = 0;
	
		ptr = malloc_f(size);

	// main --> f1() --> f2() --> f3() { __builtin_return_address(0)  }

		void *caller = __builtin_return_address(0);

		char filename[128] = {0};
		sprintf(filename, "./mem/%p.mem", ptr);

		FILE *fp = fopen(filename, "w");
		fprintf(fp, "[+] caller: %p, addr: %p, size: %ld\n",
			 COnvertToELF(caller), ptr, size);


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

void free(void *ptr) {

	if (enable_free_hook) {
		enable_free_hook = 0;
        
        char file[128] = {0};
		sprintf(file, "./mem/%p.mem", ptr);

		if (unlink(file) < 0) { // filename no exist;
			printf("double free: %p\n", ptr);
			return ;
		}

		free_f(ptr);

		enable_free_hook = 1;
	} else {

		free_f(ptr);
		
	}

}


void init_hook(void) {

	if (!malloc_f) {
		malloc_f = dlsym(RTLD_NEXT, "malloc");
	}
	if (!free_f) {
		free_f = dlsym(RTLD_NEXT, "free");
	}
}



int main(void) {

    init_hook();

    void *p1 = malloc(8);
    void *p2 = malloc(16);
    void *p3 = malloc(32);

    free(p1);
    free(p2);

    return 0;
}

编译执行:

内存泄漏检测组件的实现_第1张图片

 表示在memleak.c 文件的 main 函数中, 第149行出现内存泄漏问题。

源文件 memleak.c 


#define _GNU_SOURCE
#include 

#include 
#include 
#include 
#include 


// 方式一:宏定义
#if 0

void *_malloc(size_t size, char *filename,int line) {

    void *ptr = malloc(size);
    
    char file[128] = {0};
    sprintf(file ,"./mem/%p.mem" ,ptr);
    FILE *fp = fopen(file ,"w");//w小写

    fprintf(fp, "[+]addr: %p filename: %s line: %d\n",ptr,filename,line);

    fflush(fp);
    fclose(fp);

    return ptr;
}

void _free(void *ptr, char *filename,int line) {

    char file[128] = {0};
    sprintf(file,"./mem/%p.mem",ptr);

    if(unlink(file) < 0){//unlink用于在文件系统中删除指定的文件
        printf("double free %p\n",ptr);
        return;
    }

    return free(ptr);

}

// __FILE__ 获取文件名
// __LINE__ 获取函数执行的行号

#define malloc(size)   _malloc(size, __FILE__,__LINE__)
#define free(ptr)      _free(ptr, __FILE__,__LINE__)

//方式二:hook
#elif 1
// gcc -o memleak memleak.c -g -ldl
// addr2line -f -e ./memleak -a 0x400b38

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

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


int enable_malloc_hook = 1;
int enable_free_hook = 1;

void *COnvertToELF(void *addr) {
	Dl_info info;
	struct link_map *link;

	dladdr1(addr, &info, (void**)&link,RTLD_DL_LINKMAP);

	return (void*)((size_t)addr - link->l_addr);
}

void *malloc(size_t size) {

	void *ptr = NULL;
	if (enable_malloc_hook) {
		enable_malloc_hook = 0;
	
		ptr = malloc_f(size);

	// main --> f1() --> f2() --> f3() { __builtin_return_address(0)  }

		void *caller = __builtin_return_address(0);

		char filename[128] = {0};
		sprintf(filename, "./mem/%p.mem", ptr);

		FILE *fp = fopen(filename, "w");
		fprintf(fp, "[+] caller: %p, addr: %p, size: %ld\n",
			 COnvertToELF(caller), ptr, size);


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

void free(void *ptr) {

	if (enable_free_hook) {
		enable_free_hook = 0;
        
        char file[128] = {0};
		sprintf(file, "./mem/%p.mem", ptr);

		if (unlink(file) < 0) { // filename no exist;
			printf("double free: %p\n", ptr);
			return ;
		}

		free_f(ptr);

		enable_free_hook = 1;
	} else {

		free_f(ptr);
		
	}

}


void init_hook(void) {

	if (!malloc_f) {
		malloc_f = dlsym(RTLD_NEXT, "malloc");
	}
	if (!free_f) {
		free_f = dlsym(RTLD_NEXT, "free");
	}
}


#endif

#if 1
int main(void) {

    init_hook();

    void *p1 = malloc(8);
    void *p2 = malloc(16);
    void *p3 = malloc(32);

    free(p1);
    free(p2);

    return 0;
}

#endif

注意:提前在程序目录下创建mem文件夹,编译时添加 -g -ldl

具体使用时只需将该文件的main函数注释掉,与需要检测的程序源文件一起编译执行即可。 

你可能感兴趣的:(linux,c语言,ubuntu,开发语言)