转载自:http://blog.csdn.net/yichigo/article/details/45092265

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

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

 

 

基本原理:
1.利用glibc提供的__malloc_hook, __free_hook系列函数对内存分配是否做监控;(详见glibc的官方文档)
2.利用backtrace函数获取函数调用栈,并记录;
3.利用backtrace_symbols对调用栈对应的函数做解析;
进一步处理:
4.使用abi::__cxa_demangle把函数名解析为源代码风格;
5.使用addr2line解析出函数调用栈对应的代码行;
6.对于动态库(.so)中的地址解析,需要先在/proc/<pid>/maps文件中找到动态库映射的基地址,才能做解析。
注意:
编译连接参数中使用-g -rdynamic
 
以上每步具体实现的代码可能都没有达到最优,甚至可能是笨办法,如果有更好的实现方案请直接替换,也欢迎赐教。

示例代码:
leakmom.cpp

/* Prototypes for __malloc_hook, __free_hook */
#include  <malloc.h>
#include  <map>
#include  <utility>
#include  <execinfo.h>
#include  <errno.h>
#include    <assert.h>
#include  <cxxabi.h>
#include  <sys/types.h>
#include  <unistd.h>
#include  <stdlib.h>
#include  "leakmon.h"

CMutexLock  gLock  ;
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  ();