zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Linux下c++程序内存泄漏检测代码范例

LinuxC++内存程序代码 检测 泄漏 范例
2023-09-27 14:23:40 时间
Linux下对于程序内存泄漏检测的方法很多,最常用的的莫过于使用valgrind工具。但是valgrind相当于让程序在虚拟机中运行,会带 来较大的系统资源开销,还会对程序的运行效率产生较大影响,对于那种资源占用大的程序,如果需要长时间运行才能暴露的泄漏问题,它就显得不太好用。

linux下的c++程序中自己实现一个轻量级的泄漏检测代码其实是比较方便的,下面我就给出一个简单的范例,并作简单的说明。当然,我们还是应该提倡使用共享指针,用共享指针自动管理内存可以避免内存泄漏这样的不必要的麻烦。

Linux下c++程序内存泄漏检测代码范例

基本原理: 利用glibc提供的__malloc_hook, __free_hook系列函数对内存分配是否做监控;(详见glibc的官方文档) 利用backtrace函数获取函数调用栈,并记录; 利用backtrace_symbols对调用栈对应的函数做解析;

进一步处理:

 使用abi::__cxa_demangle把函数名解析为源代码风格;  使用addr2line解析出函数调用栈对应的代码行;  对于动态库(.so)中的地址解析,需要先在/proc/ pid /maps文件中找到动态库映射的基地址,才能做解析。

注意:

编译连接参数中使用-g -rdynamic

以上每步具体实现的代码可能都没有达到最优,甚至可能是笨办法,如果有更好的实现方案请直接替换,也欢迎赐教。

示例代码: leakmom.cpp


std::map  void*, _PtrInfo  gPtrInfo ;  std::map  const LmCallStack*, _AllocInfo ,  __comp  gLeakInfo;  const int LmCallStack:: MAX_STACK_LAYERS = 32;  /* Prototypes for our hooks. */  static void my_init_hook ( void);  static void *my_malloc_hook ( size_t, const void *);  static void my_free_hook ( void*, const void *);  void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)( size_t __size , const void *) ;  void (*__MALLOC_HOOK_VOLATILE old_free_hook) ( void *__ptr , const void *);  /* Override initializing hook from the C library. */  void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) ( void) = my_init_hook;  void my_init_hook (void)      old_malloc_hook = __malloc_hook ;      old_free_hook = __free_hook ;      __malloc_hook = my_malloc_hook ;      __free_hook = my_free_hook ;  static void *my_malloc_hook ( size_t size , const void *caller )      void *result ;         gLock.lock ();      /* Restore all old hooks */      __malloc_hook = old_malloc_hook ;      __free_hook = old_free_hook ;      /* Call recursively */      result = malloc (size);      /* Save underlying hooks */      old_malloc_hook = __malloc_hook ;      old_free_hook = __free_hook ;      /* printf might call malloc, so protect it too. */      //printf ("malloc (%u) returns %p\n", (unsigned int) size, result);         RecordPtr( result , size);      /* Restore our own hooks */      __malloc_hook = my_malloc_hook ;      __free_hook = my_free_hook ;         gLock.unlock ();      return result ;  static void my_free_hook ( void *ptr , const void *caller )         gLock.lock ();      /* Restore all old hooks */      __malloc_hook = old_malloc_hook ;      __free_hook = old_free_hook ;      /* Call recursively */      free (ptr );      /* Save underlying hooks */      old_malloc_hook = __malloc_hook ;      old_free_hook = __free_hook ;      /* printf might call free, so protect it too. */      //printf ("freed pointer %p\n", ptr);         RemovePtr( ptr );      /* Restore our own hooks */      __malloc_hook = my_malloc_hook ;      __free_hook = my_free_hook ;         gLock.unlock ();  void RecordPtr ( void* ptr, size_t size)         // 获取调用栈         void *array [LmCallStack:: MAX_STACK_LAYERS];         int cstSize = backtrace( array, LmCallStack ::MAX_STACK_LAYERS);         // 保存指针 调用栈         LmCallStack* callstack = new LmCallStack(array , cstSize);         gLock.lock ();         std::map  const LmCallStack*, _AllocInfo ,  __comp :: iterator it = gLeakInfo.find (callstack);         if (it != gLeakInfo. end())        {               it- second .size += size;               it- second .alloc++;               _PtrInfo pi (it-  first, size );               gPtrInfo[ptr ] = pi;        }         else        {               _AllocInfo aif (size, 1, 0);               std::pair  std:: map const LmCallStack*, _AllocInfo,  __comp ::iterator , bool  ret = gLeakInfo .insert( std::pair  const LmCallStack*, _AllocInfo  (callstack, aif));                             if (ret .second)              {                     _PtrInfo pi (ret. first- first , size);                     gPtrInfo[ptr ] = pi;              }               else              {                     // failed              }        }         gLock.unlock ();  void RemovePtr ( void* ptr )         gLock.lock ();         std::map  void*, _PtrInfo ::iterator it = gPtrInfo.find (ptr);         if (it != gPtrInfo. end())        {               std::map  const LmCallStack*, _AllocInfo ,  __comp :: iterator itc = gLeakInfo .find( it- second .csk);               if (itc != gLeakInfo. end())              {                     itc- second .size -= it- second .size;                     itc- second .free++;                     if (0 == (itc - second. alloc - itc - second. free))                    {                           assert(0 == itc - second. size);                           delete itc - first;                           gLeakInfo.erase (itc);                    }              }               gPtrInfo.erase (it);        }         gLock.unlock ();  void Report ()         char **strings = NULL;         gLock.lock ();         __malloc_hook = old_malloc_hook ;      __free_hook = old_free_hook ;         for (std ::map  const LmCallStack *, _AllocInfo,  __comp ::iterator it = gLeakInfo .begin();               it != gLeakInfo .end();               it++)        {               printf("\n" );               printf("====   size: %ld,  allocs: %d,  frees: %d, a-f: %d\n", it-  second.size , it-  second.alloc , it-  second.free , it- second .alloc- it- second .free );               printf("====   stacks back trace:\n" );               strings = backtrace_symbols ((void**) it- first - callstack, it- first - size);               if (strings )              {                     for(int i = 2; i   it - first-  size; i ++)                    { //printf("     %s\n", strings[i]);                           char output [1024] = {0};                           memset(output , 0, 1024);                           char temp [1024] = {0};                           memset(temp , 0, 1024);                           ////                           ////    get real function name                           ////                           if (1 == sscanf (strings[ i], "%*[^(]%*[^_]%[^)+]" , temp))                          {                                       int status ;                                       char* realname = abi::__cxa_demangle (temp, 0, 0,   status);                                       if (0 == status )                                      {                                             char* p = strchr( strings[i ], ();                                             memcpy(output , strings[ i], p-strings [i]);                                             sprintf(output +(p- strings[i ]), "(%s+%p) " , realname, (( void**)it - first-  callstack)[i ]); //printf("     -%s\n", realname);                                             free(realname );                                      }                                       else                                      {                                             char* p = strchr( strings[i ], ));                                             memcpy(output , strings[ i], p-strings [i]+2);                                      }                          }                           else                          {                                 char* p = strchr( strings[i ], ));                                 memcpy(output , strings[ i], p-strings [i]+2);                          }                           FILE * fp ;                           char module [1024] = {0};                           memset(module , 0, 1024);                           char* pm = strchr( strings[i ], ();                           memcpy(module , strings[ i], pm -strings[ i]);                           if (strstr (module, ".so"))                          {                                 __pid_t pid = getpid();                                 sprintf(temp , "grep %s /proc/%d/maps", module, pid );                                 ///                                 ///         get library base-map-address                                 ///                                 fp = popen (temp, "r");                                 if (fp )                                {                                       char baseaddr [64];                                       unsigned long long base;                                                                             fgets(temp , sizeof( temp)-1, fp );  //printf("memmap: %s\n", temp);                                       sscanf(temp , "%[^-]", baseaddr);                                       base = strtoll (baseaddr, NULL, 16); //printf("baseaddr:%s\n", baseaddr); //printf(" base:0x%llx\n", base);                                       sprintf(temp , "addr2line -e %s %p", module, (void *)((unsigned long long)((void **)it-  first- callstack )[i]- base));                                }                          }                           else                          {                                 sprintf(temp , "addr2line -e %s %p", module, ((void **)it-  first- callstack )[i]);                          }                           ////                           ////    get source file name and line number                           ////                           fp = popen (temp, "r");  //printf("cmdline: %s\n", temp);                           if (fp )                          {                                 fgets(temp , sizeof( temp)-1, fp ); //printf("     -%s\n", temp);                                 strcat(output , temp);                                 printf("   -   %s" , output);                                 pclose(fp );                          }                           else                          {                                 printf("   -   %s\n" , output);                          }                    }                     free(strings );                     strings = NULL ;              }        }         __malloc_hook = my_malloc_hook ;      __free_hook = my_free_hook ;         gLock.unlock ();  //////////////////////////////////////////////////////////////////////////  CMutexLock::CMutexLock ()         pthread_mutexattr_t  m_attr ;         pthread_mutexattr_init( m_attr );         pthread_mutexattr_settype( m_attr , PTHREAD_MUTEX_RECURSIVE);         if (0 != pthread_mutex_init ( m_mutex ,   m_attr))        {               printf("c_lock::c_lock pthread_mutex_init error %d .\n" , errno);               assert(0);        }         pthread_mutexattr_destroy( m_attr );  CMutexLock::~CMutexLock ()         if(0 != pthread_mutex_destroy ( m_mutex))        {               printf("c_lock::~c_lock pthread_mutex_destroy error %d .\n" , errno);               assert(0);        }  void  CMutexLock::lock ()         if(0 != pthread_mutex_lock ( m_mutex))        {               assert("c_lock::lock pthread_mutex_lock "   0);        }  void  CMutexLock::unlock ()         int iRet = 0;         if(0 != (iRet = pthread_mutex_unlock(  m_mutex)))        {               printf("c_lock::unlock pthread_mutex_unlock ret %d  error %d .\n", iRet, errno );               assert(0);        }  示例代码:  leakmom.h  ////////////////////////////////////////////////////////////////////////  //    The Executable file MUST be linked with parameter -rdynamic !!!  ////////////////////////////////////////////////////////////////////////  #pragma once  #include  string.h   #include  pthread.h                             class LmCallStack  public:         char* callstack ; // pointer to buffer recording callstack addresses         int size ; // count of call stacks         static const int MAX_STACK_LAYERS;  public:         LmCallStack(void * csk= NULL, int s=0)        {               if (csk )              {                     callstack = new char[ s*sizeof (void*)];                     memcpy(callstack , csk, s*sizeof (void*));              }               else              {                     callstack = (char *)csk;              }               size = s ;        }        ~ LmCallStack()        {               if (callstack )              {                     delete[] callstack ;              }               callstack = NULL ;               size = 0;        }  class __comp  public:         __comp(){};         bool operator () (const LmCallStack* first , const LmCallStack* second)        {               return ((first - size   second- size ) ||                          ( first- size == second-  size                                         memcmp(first - callstack, second- callstack , sizeof( void*)*first - size)   0)                          );        }  struct _PtrInfo         _PtrInfo(const LmCallStack* c=NULL , long s=0)        {               csk = c ;               size = s ;        }         const LmCallStack * csk;         long size ;  struct _AllocInfo         _AllocInfo(long s=0, int a =0, int f=0)        {               size=s ;               alloc=a ;               free=f ;        }         //         long size ;         int alloc ;         int free ;  class CMutexLock  public:         CMutexLock();        ~ CMutexLock();  public:         void lock ();         void unlock ();  private:         pthread_mutex_t m_mutex ;  void RecordPtr ( void* ptr, size_t size);  void RemovePtr (void* ptr);  void Report (); 
Android C++系列:Linux Socket编程(四)多路IO转接服务器 select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开 的文件描述符个数并不能改变select监听文件个数
Android C++ 系列:Linux Socket 编程(三)CS 模型示例 服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于 监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服 务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK 段,服务器收到后从accept()返回。
Android C++ 系列:Linux Socket 编程(二)网络套接字函数 socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描 述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调 用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表 示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
Android C++ 系列:Linux Socket 编程(一)预备知识 为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运 行,可以调用以下库函数做网络字节序和主机字节序的转换。
Android C++ 系列:Linux 常用函数和工具 如果times是非空指针,则存取时间和修改时间被设置为 times所指向的结构中的值。此 时,进程的有效用户ID必须等于该文件的所有者 ID,或者进程必须是一个超级用户进程。对 文件只具有写许可权是不够的
Android C++系列:Linux信号(三) 例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。
Android C++系列:Linux信号(二) 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非 空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针, 则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前 的信号屏蔽字为mask,下表说明了how参数的可选值。
Android C++系列:Linux进程间通信(二) mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存 地址,对文件的读写可以直接用指针来做而不需要read/write函数。
Android C++系列:Linux进程间通信(一) 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不 到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用 户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程 间通信(IPC,InterProcess Communication)。