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