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

2023-05-16

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

 
最后给出测试代码作为使用范例:
 
testso.cpp和testso.h需要编译为动态库(.so),由于测试动态库中的内存分配
testso.cpp源码:
 
#include "testso.h"

void testsoleak()
{
 char* p = new char[15];
}


testso.h源码:

void testsoleak();

 
test.cpp源码:
 
#include "leakmon.h"
#include "testso.h"

char* func1()
{
	char* p = new char[10];
	return p;
}

int main (int argc, char *argv[])
{
	gStartLeakMon = 1;

	char* p = new char[3];

	for (int i=0; i<10; i++)
	{
		p = func1();
		delete[] p;
	}

	for (int i=0; i<5; i++)
	{
		testsoleak();
	}

	Report();
	return(0);
}


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux下c++程序内存泄漏检测代码范例 的相关文章

随机推荐