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

来源:互联网 发布:淘宝卖家如何发布预览 编辑:程序博客网 时间:2024/06/08 18:38

转载自: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 ();
0 0