linux C内存泄露检测实现及内存泄露检测的一般方法

2019-07-13 03:27发布

linux中,由于使用malloc或alloc而没有free掉申请的内存,就会造成内存的泄露。通常,来讲为了避免内存泄露的情况出现,一般要求,我们尽量的malloc之后,调用free。但是总会有忘记free的时候啊。一般可以有如下几种方式来避免内存泄露: 1)  使用智能指针,这个在C++中较为常见; 2)  使用内存池; 3)  自己封装一层malloc/free等等。当申请内存时,将申请信息放入到一个已分配内存信息链表里面。free时,删除对应的信息表的节点。在程序执行结束前,扫瞄该信息表,若还存在信息节点,那么该节点记录的就是泄露的内存信息。若链表为空,那就是说没有发生内存泄露; 4)使用检测工具检测内存泄露,进而修补程序,这样的工具有比如Valgrind等等。 以下,给出一个按方式3实现的内存泄露检测的实现版本,代码量本身不多。 leak_detector_c.h文件 #ifndef LEAK_DETECTOR_C_H #define LEAK_DETECTOR_C_H #define FILE_NAME_LENGTH 256 #define OUTPUT_FILE "/home/leak_info.txt" /*this file should be created first!*/ #define malloc(size) xmalloc (size, __FILE__, __LINE__) #define calloc(elements, size) xcalloc (elements, size, __FILE__, __LINE__) #define free(mem_ref) xfree(mem_ref) struct _MEM_INFO { void *address; unsigned int size; char file_name[FILE_NAME_LENGTH]; unsigned int line; }; typedef struct _MEM_INFO MEM_INFO; struct _MEM_LEAK { MEM_INFO mem_info; struct _MEM_LEAK * next; }; typedef struct _MEM_LEAK MEM_LEAK; void add(MEM_INFO alloc_info); void erase(unsigned pos); void clear(void); void * xmalloc(unsigned int size, const char * file, unsigned int line); void * xcalloc(unsigned int elements, unsigned int size, const char * file, unsigned int line); void xfree(void * mem_ref); void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line); void remove_mem_info (void * mem_ref); void report_mem_leak(void); #endif leak_detector_c.c文件 #include #include #include #include "leak_detector_c.h" #undef malloc #undef calloc #undef free static MEM_LEAK * ptr_start = NULL; static MEM_LEAK * ptr_next = NULL; /* * adds allocated memory info. into the list * */ void add(MEM_INFO alloc_info) { 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->next = NULL; if (ptr_start == NULL) { ptr_start = mem_leak_info; ptr_next = ptr_start; } else { ptr_next->next = mem_leak_info; ptr_next = ptr_next->next; } } /* * erases memory info. from the list * */ void erase(unsigned pos) { unsigned index = 0; MEM_LEAK * alloc_info, * temp; if(pos == 0) { MEM_LEAK * temp = ptr_start; ptr_start = ptr_start->next; free(temp); } else { for(index = 0, alloc_info = ptr_start; index < pos; alloc_info = alloc_info->next, ++index) { if(pos == index + 1) { temp = alloc_info->next; alloc_info->next = temp->next; if(temp->next==NULL)                   {                     ptr_next = alloc_info;                   } free(temp); break; } } } } /* * deletes all the elements from the list */ void clear() { MEM_LEAK * temp = ptr_start; MEM_LEAK * alloc_info = ptr_start; while(alloc_info != NULL) { alloc_info = alloc_info->next; free(temp); temp = alloc_info; } ptr_start=NULL; } /* * replacement of malloc */ void * xmalloc (unsigned int size, const char * file, unsigned int line) { void * ptr = malloc (size); if (ptr != NULL) { add_mem_info(ptr, size, file, line); } return ptr; } /* * replacement of calloc */ void * xcalloc (unsigned int elements, unsigned int size, const char * file, unsigned int line) { unsigned total_size; void * ptr = calloc(elements , size); if(ptr != NULL) { total_size = elements * size; add_mem_info (ptr, total_size, file, line); } return ptr; } /* * replacement of free */ void xfree(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_INFO mem_alloc_info; /* fill up the structure with all info */ memset( &mem_alloc_info, 0, sizeof ( mem_alloc_info ) ); mem_alloc_info.address = mem_ref; mem_alloc_info.size = size; strncpy(mem_alloc_info.file_name, file, FILE_NAME_LENGTH); mem_alloc_info.line = line; /* 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) { unsigned short index; MEM_LEAK * leak_info = ptr_start; /* check if allocate memory is in our list */ for(index = 0; leak_info != NULL; ++index, leak_info = leak_info->next) { if ( leak_info->mem_info.address == mem_ref ) { erase ( index ); break; } } } /* * writes all info of the unallocated memory into a file */  void report_mem_leak(void) {     unsigned short index;     MEM_LEAK * leak_info;     FILE * fp_write = fopen (OUTPUT_FILE, "w+");     char info[1024];      if(fp_write != NULL)      {          sprintf(info, "%s ", "Memory Leak Summary");          fwrite(info, (strlen(info)) , 1, fp_write);          sprintf(info, "%s ", "-----------------------------------");          fwrite(info, (strlen(info)) , 1, fp_write);         for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next)         {              sprintf(info, "address : %d ", leak_info->mem_info.address);              fwrite(info, (strlen(info)) , 1, fp_write);              sprintf(info, "size    : %d bytes ", leak_info->mem_info.size);              fwrite(info, (strlen(info)) , 1, fp_write);              sprintf(info, "file    : %s ", leak_info->mem_info.file_name);              fwrite(info, (strlen(info)) , 1, fp_write);              sprintf(info, "line    : %d ", leak_info->mem_info.line);              fwrite(info, (strlen(info)) , 1, fp_write);              sprintf(info, "%s ", "-----------------------------------");              fwrite(info, (strlen(info)) , 1, fp_write);              printf("addr=%d,size=%d,file=%s,line=%d "                 ,leak_info->mem_info.address                 ,leak_info->mem_info.size                 ,leak_info->mem_info.file_name                 ,leak_info->mem_info.line);         }         fclose(fp_write);       }       else       {       }       /*clear();*/ } 使用时,只需将以上两个文件放入到你的工程目录下,并在主函数里面调用report_mem_leak就OK了。 测试test.c文件 #include "leak_detector_c.h" int main() { char * ptr1 = (char *)malloc(10); int * ptr2 = (int *)calloc(10, sizeof(int)); float * ptr3 = (float *) calloc(15, sizeof(float)); free(ptr2); atexit(report_mem_leak); return 0; } 编译test.c并运行,可以在/home/leak_info.txt文件中,看到两条内存泄露记录。 总的来说,这样的检测方式还是有一定效果的,但是同时内存信息链表节点也使用了malloc来申请内存,这就造成了一定的额外的内存开销。 我觉得要避免内存泄露,首先是自身要养成良好的编程习惯,然后是使用相关的方法和工具对代码进行检测,最后是要阅读代码了。工具也并不能检测出所有的内存泄露,在这个时候只能靠自己去阅读代码分析代码了,人毕竟比机器灵活嘛!  

Valgrind移植powerpc


./autogen.sh


./configure --host=powerpc-linux CC=powerpc-linux-gnu-gcc CPP=powerpc-linux-gnu-cpp CXX=powerpc-linux-gnu-g++ --prefix=/mnt/nand


make


make install

 ./valgrind --tool=memcheck --vgdb=no ./memleak 

注意: 1. --prefix=/mnt/nand 指定的目录要与开发板上放置的目录一致
2. 参数必须  --vgdb=no 否则会如下错误(可能因为我嵌入式linux 没有集成gdb)
root@xxx:/mnt/nand/bin# ./valgrind --tool=memcheck ./memleak 
==1300== Memcheck, a memory error detector
==1300== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1300== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==1300== Command: ./memleak
==1300== 
==1300== error 2 No such file or directory
==1300== mknod /tmp/vgdb-pipe-from-vgdb-to-1300-by-root-on-/bin/hostname
==1300== valgrind: fatal error: vgdb FIFOs cannot be created.