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

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

 

/* 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_tconst 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 , sizeofvoid*)*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 (); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.
  • 335.
  • 336.
  • 337.
  • 338.
  • 339.
  • 340.
  • 341.
  • 342.
  • 343.
  • 344.
  • 345.
  • 346.
  • 347.
  • 348.
  • 349.
  • 350.
  • 351.
  • 352.
  • 353.
  • 354.
  • 355.
  • 356.
  • 357.
  • 358.
  • 359.
  • 360.
  • 361.
  • 362.
  • 363.
  • 364.
  • 365.
  • 366.
  • 367.
  • 368.
  • 369.
  • 370.
  • 371.
  • 372.
  • 373.
  • 374.
  • 375.
  • 376.
  • 377.
  • 378.
  • 379.
  • 380.
  • 381.
  • 382.
  • 383.
  • 384.
  • 385.
  • 386.
  • 387.
  • 388.
  • 389.
  • 390.
  • 391.
  • 392.
  • 393.
  • 394.
  • 395.
  • 396.
  • 397.
  • 398.
  • 399.
  • 400.
  • 401.
  • 402.
  • 403.
  • 404.
  • 405.
  • 406.
  • 407.
  • 408.
  • 409.
  • 410.
 
 
 
责任编辑:王雪燕 来源: yichigo的博客
相关推荐

2011-06-16 09:28:02

C++内存泄漏

2024-07-03 11:28:15

2025-03-03 00:00:00

2011-08-15 10:16:55

内存泄露

2013-08-02 09:52:14

AndroidApp内存泄漏

2019-09-29 00:25:11

CC++内存泄漏

2010-02-04 10:19:39

C++多线程

2024-04-19 08:00:00

2017-09-07 16:52:23

2021-03-26 05:59:10

内存检测工具

2010-02-01 14:26:50

C++读写文本文件

2024-12-19 14:42:15

C++内存泄漏内存管理

2010-09-25 11:07:45

Java内存泄漏

2015-07-10 09:15:47

LeakCanary内存泄漏

2018-12-07 10:52:08

内存泄漏方法

2023-10-31 16:40:38

LeakCanary内存泄漏

2020-11-02 09:48:35

C++泄漏代码

2010-02-03 10:17:29

C++继承方式

2009-06-16 11:20:22

内存泄漏

2012-09-21 10:30:56

Linux项目代码覆盖率
点赞
收藏

51CTO技术栈公众号