|
在C/C++程序开发的时候经常需要使用malloc/free对内存进行申请与释放,如果在使用malloc后却忘记了调用free对内存进行释放,此时就造成了内存泄露,随着时间的推移会慢慢消耗掉系统所有的内存,导致程序无法继续运行,而往往这种内存泄露的问题在大型项目中是很难被定位的,如何通过自定义函数的方法很容易地检测到内存的泄露呢?
主要解决方法为:通过自定义内存申请和释放的方法mymalloc和myfree,然后设置一个钩子,让用户在调用malloc/free的时候会调用自定义的myalloc/free函数,然后通过下面的宏定义实现当malloc被调用的时候就会默认调用mymalloc函数的功能:
- #define malloc(size) mymalloc (size, __FILE__, __LINE__)
复制代码 需要注意的是在调用mymalloc的时候多传入了两个参数__FILE__和 __LINE__,它们分别表示调用mymalloc的代码所在的文件以及行号。有了这些信息以后,主要的实现方法为: 自定义一个列表,每当malloc函数被调用的时候,mymalloc被调用的时候,首先通过调用系统函数malloc把内存申请好,然后把调用者的信息(调用者所在的文件和行号)以及申请好内存的信息添加到列表中;当myfree被调用的时候,首先通过调用系统函数free释放的对应的内存,然后根据待释放的内存地址在列表中删除记录这块内存申请的信息。实现代码如下:
leak_detector.h
- #ifndef LEAK_DETECTOR_C_H
- #define LEAK_DETECTOR_C_H
- #pragma (report_mem_leak)
- #define FILE_NAME_LENGTH 256 //used to store file name in whitch calls malloc
- #define CALL_DEPTH 10 //repesesent the max fuction call level
- #define TIME_LEN 26 //time string length
- #define OUTPUT_FILE "leak_info.txt" //store memory leak info
- #define malloc(size) mymalloc (size, __FILE__, __LINE__)
- #define calloc(elements, size) mycalloc (elements, size, __FILE__, __LINE__)
- #define realloc(address, size) myrealloc(address, size, __FILE__, __LINE__)
- #define free(mem_ref) myfree(mem_ref)
- struct _MEM_TRACE_INFO {
- char** traceInfo; //backtrace information malloc is called
- size_t size; //number of track infos
- };
- typedef struct _MEM_TRACE_INFO MEM_TRACE_INFO;
- struct _MEM_INFO
- {
- void *address;
- unsigned int size;
- char file_name[FILE_NAME_LENGTH];
- unsigned int line;
- char allocTime[TIME_LEN];
- MEM_TRACE_INFO* traceInfo;
- };
- typedef struct _MEM_INFO MEM_INFO;
- struct _MEM_LEAK {
- MEM_INFO mem_info;
- struct _MEM_LEAK * next;
- };
- typedef struct _MEM_LEAK MEM_LEAK;
- static void add(MEM_INFO alloc_info); //add a allocated info to the linkedlist
- static void erase(void * address); //remove a allocted info from the linkedlist
- static void clear(void); //free the heap space that stores the memory leak info
- void * mymalloc(unsigned int size, const char * file, unsigned int line); //replace malloc
- void * mycalloc(unsigned int elements, unsigned int size, const char * file, unsigned int line); //replace calloc
- void * myrealloc(void* address, unsigned int size, const char * file, unsigned int line); //replace realloc
- void myfree(void * mem_ref); //replace free
- static void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line,MEM_TRACE_INFO* traceinfo);
- static void remove_mem_info (void * mem_ref);
- void report_mem_leak(void) __attribute__((destructor));
- #endif
复制代码 leak_detector.c:
- #include <stdio.h>
- #include <malloc.h>
- #include <string.h>
- #include <time.h>
- #include <execinfo.h>
- #include "leak_detector.h"
- #undef malloc
- #undef calloc
- #undef realloc
- #undef free
- static MEM_LEAK * ptr_start = NULL; //list head
- static MEM_LEAK * ptr_end = NULL; //list tail
- /*
- * get function call trace information
- * return a pointer to trace info
- */
- MEM_TRACE_INFO* getTrackInfo() {
- void *array[CALL_DEPTH];
- size_t size;
- size = backtrace(array, 10);
- char** trace=backtrace_symbols(array, size);
- MEM_TRACE_INFO* traceInfo=(MEM_TRACE_INFO*)malloc(sizeof(MEM_TRACE_INFO));
- if(traceInfo == NULL)
- return NULL;
- traceInfo->size=size;
- traceInfo->traceInfo=trace;
- return traceInfo;
- }
- /*
- * adds allocated memory info into likedlist
- *
- */
- void add(MEM_INFO alloc_info)
- {
- printf("add new allocation info:%d\n",(int)alloc_info.address);
- MEM_LEAK* mem_leak_info=NULL;
- mem_leak_info=(MEM_LEAK*)malloc(sizeof(MEM_LEAK));
- mem_leak_info->mem_info.address=alloc_info.address;
- mem_leak_info->mem_info.size=alloc_info.size;
- strcpy(mem_leak_info->mem_info.file_name,alloc_info.file_name);
- mem_leak_info->mem_info.line=alloc_info.line;
- mem_leak_info->mem_info.traceInfo=alloc_info.traceInfo;
- strncpy(mem_leak_info->mem_info.allocTime, alloc_info.allocTime,TIME_LEN);
- if (ptr_start == NULL) //the list has no node
- {
- ptr_start = mem_leak_info;
- ptr_end = ptr_start;
- }
- else { //add allocated info into the tail of list
- ptr_end->next = mem_leak_info;
- ptr_end = ptr_end->next;
- }
- }
- /*
- * remove memory info(address) from the list
- *
- */
- void erase(void* address){
- MEM_LEAK * temp = ptr_start;
- MEM_LEAK * pre;
- if(temp != NULL && temp->mem_info.address == address){ //if the node is the head of the list
- ptr_start=temp->next;
- free(temp->mem_info.traceInfo); //release space that stores traceInfo
- free(temp); //release space that stores list node
- return;
- }
- if(temp->next != NULL){
- pre=temp;
- temp=temp->next;
- for(; temp != NULL; pre=temp, temp=temp->next){
- if(temp->mem_info.address == address){ //find the node
- pre->next = temp->next;
- if(temp->next == NULL) //the node to be removed is the tail of the list
- ptr_end=pre;
- free(temp->mem_info.traceInfo); //free space for traceinfo
- free(temp); //free space for list node
- break;
- }
- }
- }
- }
- /*
- * deletes all the elements from the list and free the space
- */
- void clear()
- {
- MEM_LEAK * temp = ptr_start;
- MEM_LEAK * alloc_info = ptr_start;
- while(alloc_info != NULL)
- {
- alloc_info = alloc_info->next;
- free(temp->mem_info.traceInfo);
- free(temp);
- temp = alloc_info;
- }
- }
- /*
- * replacement of malloc
- */
- void * mymalloc (unsigned int size, const char * file, unsigned int line)
- {
- void * ptr = malloc (size);
- if (ptr != NULL)
- {
- MEM_TRACE_INFO* traceinfo=getTrackInfo();
- add_mem_info(ptr, size, file, line,traceinfo);
- }
- return ptr;
- }
- /*
- * replacement of calloc
- */
- void * mycalloc (unsigned int elements, unsigned int size, const char * file, unsigned int line)
- {
- unsigned total_size;
- void * ptr = calloc(elements , size);
- if(ptr != NULL)
- {
- MEM_TRACE_INFO* traceinfo=getTrackInfo();
- total_size = elements * size;
- add_mem_info (ptr, total_size, file, line,traceinfo);
- }
- return ptr;
- }
- /*
- * replacement of malloc
- */
- void* myrealloc(void* address, unsigned int size, const char * file, unsigned int line){
- printf("realloc\n");
- unsigned total_size;
- void* ptr=realloc(address,size);
- if(ptr!=NULL){
- MEM_TRACE_INFO* traceinfo=getTrackInfo();
- remove_mem_info(address);
- add_mem_info(address, size, file, line ,traceinfo);
- }
- }
- /*
- * replacement of free
- */
- void myfree(void * mem_ref)
- {
- remove_mem_info(mem_ref);
- free(mem_ref);
- }
- /*
- * gets the allocated memory info and adds it to a list
- *
- */
- void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line,MEM_TRACE_INFO* traceinfo)
- {
- MEM_INFO mem_alloc_info;
- memset(&mem_alloc_info,0,sizeof(mem_alloc_info));
- mem_alloc_info.address = mem_ref;
- mem_alloc_info.size = size;
- strcpy(mem_alloc_info.file_name, file);
- mem_alloc_info.line = line;
- mem_alloc_info.traceInfo=traceinfo;
- time_t timeP = time(0);
- char* t=ctime(&timeP);
- strncpy(mem_alloc_info.allocTime, t,TIME_LEN);
- /* add the above info to a list */
- add(mem_alloc_info);
- }
- /*
- * if the allocated memory info is part of the list, removes it
- *
- */
- void remove_mem_info (void * mem_ref)
- {
- erase(mem_ref);
- }
- /*
- * writes all info of the unallocated memory into a file
- */
- void report_mem_leak(void)
- {
- unsigned int i;
- printf("%s","mem_leak");
- unsigned short index;
- MEM_LEAK * leak_info;
- FILE * fp_write = fopen (OUTPUT_FILE, "w");
- char info[1024];
- if(fp_write != NULL)
- {
- fprintf(fp_write, "%s\n", "Memory Leak Summary");
- fprintf(fp_write, "%s\n", "-----------------------------------");
- for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next)
- {
- fprintf(fp_write, "address : %x\n", (int)leak_info->mem_info.address);
- fprintf(fp_write, "size : %d bytes\n", leak_info->mem_info.size);
- fprintf(fp_write, "file : %s\n", leak_info->mem_info.file_name);
- fprintf(fp_write, "line : %d\n", leak_info->mem_info.line);
- fprintf(fp_write, "Time : %s\n", leak_info->mem_info.allocTime);
- fprintf(fp_write, "traceInfo :\n");
- for(i=2;i<leak_info->mem_info.traceInfo->size-2;i++){
- fprintf(fp_write, " %s\n", leak_info->mem_info.traceInfo->traceInfo[i]);
- }
- fprintf(fp_write, "%s\n", "-----------------------------------");
- }
- }
- clear();
- }
复制代码
test.c:
- /***************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include "leak_detector.h"
- /*********************************/
- /*Correct use of MALLOC and FREE */
- /*********************************/
- //Protoytpe simple function
- void modify(double *);
- void leak(int);
- //Main program
- int main()
- {
- //Define the size of Array
- int SIZE=100, i;
- //Define the array starting point pointer.
- double *array;
- //Allocate memory for doubles
- array = (double *)malloc(sizeof(double)*SIZE + 1);
- //Check the memory was allocated
- if (array==NULL){
- printf("Memory could not be allocated \n");
- }
- else {
- printf("Memory was allocated – remember to free\n \n");
- //Modify only the first 10 elements via a function.
- //Modify global variable from within function.
- modify(array);
- //Print out the first 15 values of array;
- printf("The first 10 will make sense, the next 5 wont! \n");
- for(i=0;i<15;i++){
- printf("a[%d] = %0.3f \n",i,array[i]);
- }
- }
-
- leak(6);
-
- //free the memory allocated
- free(array);
- return 0;
- }
- //Create basic function – passes the pointer to array (first position)
- void modify(double *array_diff){
- int i;
-
- for(i=0;i<10;i++){
- array_diff[i]=i/(i*i + 1);
- }
-
- leak(4);
-
- return;
- }
-
-
- void leak(int size){
- char *p=0;
-
- p = (char*)malloc(size);
-
- return;
- }
复制代码 makefile:
- test:test.c leak_detector.h leak.so
- gcc -o test test.c ./leak.so -rdynamic
- leak.so:leak_detector.o
- gcc -shared -o leak.so leak_detector.o -rdynamic
- leak_detector.o:leak_detector.h leak_detector.c
- gcc -fpic -c leak_detector.c -rdynamic
- clean:
- rm *.o *.so
复制代码 文件说明:
leak_detector.h: 提供了实现malloc和free接口的头文件。
leak_detector.c: 实现了自定义的内存申请和释放的函数。
test.c: 用来测试自定义内存申请和释放的函数
leak_info.txt: 运行结果将会保存在这个文件中
makefile: 用来编译程序。
运行方法:
make leak.so //生成 leak.so
make test //生成可执行文件 test
./test //运行test
|
|