admin 发表于 2016-11-12 14:58:01

C/C++(面试) 如何自定义函数进行内存泄露检测

         在C/C++程序开发的时候经常需要使用malloc/free对内存进行申请与释放,如果在使用malloc后却忘记了调用free对内存进行释放,此时就造成了内存泄露,随着时间的推移会慢慢消耗掉系统所有的内存,导致程序无法继续运行,而往往这种内存泄露的问题在大型项目中是很难被定位的,如何通过自定义函数的方法很容易地检测到内存的泄露呢?
      主要解决方法为:通过自定义内存申请和释放的方法mymalloc和myfree,然后设置一个钩子,让用户在调用malloc/free的时候会调用自定义的myalloc/free函数,然后通过下面的宏定义实现当malloc被调用的时候就会默认调用mymalloc函数的功能:

#definemalloc(size)               mymalloc (size, __FILE__, __LINE__)      需要注意的是在调用mymalloc的时候多传入了两个参数__FILE__和 __LINE__,它们分别表示调用mymalloc的代码所在的文件以及行号。有了这些信息以后,主要的实现方法为:      自定义一个列表,每当malloc函数被调用的时候,mymalloc被调用的时候,首先通过调用系统函数malloc把内存申请好,然后把调用者的信息(调用者所在的文件和行号)以及申请好内存的信息添加到列表中;当myfree被调用的时候,首先通过调用系统函数free释放的对应的内存,然后根据待释放的内存地址在列表中删除记录这块内存申请的信息。实现代码如下:
leak_detector.h
#ifndefLEAK_DETECTOR_C_H
#defineLEAK_DETECTOR_C_H

#pragma(report_mem_leak)
#defineFILE_NAME_LENGTH              256   //used to store file name in whitch calls malloc
#defineCALL_DEPTH               10   //repesesent the max fuction call level
#defineTIME_LEN                  26   //time string length
#defineOUTPUT_FILE                           "leak_info.txt"   //store memory leak info
#definemalloc(size)                              mymalloc (size, __FILE__, __LINE__)
#definecalloc(elements, size)        mycalloc (elements, size, __FILE__, __LINE__)
#definerealloc(address, size)   myrealloc(address, size, __FILE__, __LINE__)
#definefree(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;
    unsigned int          line;
    char            allocTime;
    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;
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;

    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);
            }
            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);
                              }
                }
               
                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 + 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


页: [1]
查看完整版本: C/C++(面试) 如何自定义函数进行内存泄露检测