您的位置:首页 > 运维架构 > Linux

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

2015-04-17 09:27 288 查看
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

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

[align=left]CMutexLock gLock ;[/align]
[align=left]std::map <void*, _PtrInfo> gPtrInfo ;[/align]
[align=left]std::map <const LmCallStack*, _AllocInfo , __comp> gLeakInfo;[/align]

const int LmCallStack:: MAX_STACK_LAYERS =
32;

[align=left]/* Prototypes for our hooks. */[/align]
[align=left]static void my_init_hook ( void);[/align]
[align=left]static void *my_malloc_hook ( size_t, const void *);[/align]
[align=left]static void my_free_hook ( void*, const void *);[/align]

void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)( size_t __size , const void *)
;
void (*__MALLOC_HOOK_VOLATILE old_free_hook)
( void *__ptr , const void *);
[align=left]/* Override initializing hook from the C library. */[/align]
void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook)
( void) = my_init_hook;

[align=left]void my_init_hook (void)[/align]
[align=left]{[/align]
[align=left] old_malloc_hook = __malloc_hook ;[/align]
[align=left] old_free_hook = __free_hook ;[/align]
[align=left] __malloc_hook = my_malloc_hook ;[/align]
[align=left] __free_hook = my_free_hook ;[/align]
[align=left]}[/align]

[align=left]static void *my_malloc_hook ( size_t size , const void *caller )[/align]
[align=left]{[/align]
[align=left] void *result ;[/align]
[align=left] gLock.lock ();[/align]
[align=left] /* Restore all old hooks */[/align]
[align=left] __malloc_hook = old_malloc_hook ;[/align]
[align=left] __free_hook = old_free_hook ;[/align]
[align=left] /* Call recursively */[/align]
[align=left] result = malloc (size);[/align]
[align=left] /* Save underlying hooks */[/align]
[align=left] old_malloc_hook = __malloc_hook ;[/align]
[align=left] old_free_hook = __free_hook ;[/align]
[align=left] /* printf might call malloc, so protect it too. */[/align]
[align=left] //printf ("malloc (%u) returns %p\n", (unsigned int) size, result);[/align]
[align=left] RecordPtr( result , size);[/align]

[align=left] /* Restore our own hooks */[/align]
[align=left] __malloc_hook = my_malloc_hook ;[/align]
[align=left] __free_hook = my_free_hook ;[/align]
[align=left] gLock.unlock ();[/align]
[align=left] return result ;[/align]
[align=left]}[/align]

[align=left]static void my_free_hook ( void *ptr , const void *caller )[/align]
[align=left]{[/align]
[align=left] gLock.lock ();[/align]
[align=left] /* Restore all old hooks */[/align]
[align=left] __malloc_hook = old_malloc_hook ;[/align]
[align=left] __free_hook = old_free_hook ;[/align]
[align=left] /* Call recursively */[/align]
[align=left] free (ptr );[/align]
[align=left] /* Save underlying hooks */[/align]
[align=left] old_malloc_hook = __malloc_hook ;[/align]
[align=left] old_free_hook = __free_hook ;[/align]
[align=left] /* printf might call free, so protect it too. */[/align]
[align=left] //printf ("freed pointer %p\n", ptr);[/align]

[align=left] RemovePtr( ptr );[/align]

[align=left] /* Restore our own hooks */[/align]
[align=left] __malloc_hook = my_malloc_hook ;[/align]
[align=left] __free_hook = my_free_hook ;[/align]
[align=left] gLock.unlock ();[/align]
[align=left]}[/align]

[align=left]void RecordPtr ( void* ptr, size_t size)[/align]
[align=left]{[/align]
[align=left] // 获取调用栈[/align]
[align=left] void *array [LmCallStack:: MAX_STACK_LAYERS];[/align]
[align=left] int cstSize = backtrace( array, LmCallStack ::MAX_STACK_LAYERS);[/align]

[align=left] // 保存指针 调用栈[/align]
[align=left] LmCallStack* callstack = new LmCallStack(array , cstSize);[/align]

[align=left] gLock.lock ();[/align]

[align=left] std::map <const LmCallStack*, _AllocInfo , __comp>:: iterator it = gLeakInfo.find (callstack);[/align]
[align=left] if (it != gLeakInfo. end())[/align]
[align=left] {[/align]
[align=left] it->second .size += size;[/align]
[align=left] it->second .alloc++;[/align]

[align=left] _PtrInfo pi (it-> first, size );[/align]
gPtrInfo[ptr ]
= pi;
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
_AllocInfo aif (size,
1, 0);
[align=left] std::pair <std:: map<const LmCallStack*, _AllocInfo, __comp>::iterator , bool> ret = gLeakInfo .insert( std::pair <const LmCallStack*, _AllocInfo >(callstack, aif));[/align]
[align=left] [/align]
[align=left] if (ret .second)[/align]
[align=left] {[/align]
[align=left] _PtrInfo pi (ret. first->first , size);[/align]
gPtrInfo[ptr ]
= pi;
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
[align=left] // failed[/align]
[align=left] }[/align]
[align=left] }[/align]

[align=left] gLock.unlock ();[/align]
[align=left]}[/align]

[align=left]void RemovePtr ( void* ptr )[/align]
[align=left]{[/align]
[align=left] gLock.lock ();[/align]

[align=left] std::map <void*, _PtrInfo>::iterator it = gPtrInfo.find (ptr);[/align]
[align=left] if (it != gPtrInfo. end())[/align]
[align=left] {[/align]
[align=left] std::map <const LmCallStack*, _AllocInfo , __comp>:: iterator itc = gLeakInfo .find( it->second .csk);[/align]
[align=left] if (itc != gLeakInfo. end())[/align]
[align=left] {[/align]
[align=left] itc->second .size -= it->second .size;[/align]
[align=left] itc->second .free++;[/align]

[align=left] if (0 == (itc ->second. alloc - itc ->second. free))[/align]
[align=left] {[/align]
[align=left] assert(0 == itc ->second. size);[/align]
[align=left] delete itc ->first;[/align]
[align=left] gLeakInfo.erase (itc);[/align]
[align=left] }[/align]
[align=left] }[/align]

[align=left] gPtrInfo.erase (it);[/align]
[align=left] }[/align]

[align=left] gLock.unlock ();[/align]
[align=left]}[/align]

[align=left]void Report ()[/align]
[align=left]{[/align]
[align=left] char **strings = NULL;[/align]
[align=left] gLock.lock ();[/align]

[align=left] __malloc_hook = old_malloc_hook ;[/align]
[align=left] __free_hook = old_free_hook ;[/align]

[align=left] for (std ::map< const LmCallStack *, _AllocInfo, __comp>::iterator it = gLeakInfo .begin();[/align]
[align=left] it != gLeakInfo .end();[/align]
[align=left] it++)[/align]
[align=left] {[/align]
[align=left] printf("\n" );[/align]
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" );
[align=left] strings = backtrace_symbols ((void**) it->first ->callstack, it->first ->size);[/align]
[align=left] if (strings )[/align]
[align=left] {[/align]
for(int i =
2; i < it ->first-> size; i ++)
[align=left] { //printf(" %s\n", strings[i]);[/align]
char output [1024]
= {0};
memset(output ,
0, 1024);
char temp [1024]
= {0};
memset(temp ,
0, 1024);
[align=left] ////[/align]
[align=left] //// get real function name[/align]
[align=left] ////[/align]
[align=left] if (1 == sscanf (strings[ i], "%*[^(]%*[^_]%[^)+]" , temp))[/align]
[align=left] {[/align]
[align=left] int status ;[/align]
char* realname = abi::__cxa_demangle (temp,
0, 0, & status);
[align=left] if (0 == status )[/align]
[align=left] {[/align]
[align=left] char* p = strchr( strings[i ], '(');[/align]
[align=left] memcpy(output , strings[ i], p-strings [i]);[/align]
sprintf(output +(p- strings[i ]), "(%s+%p)
" , realname, (( void**)it ->first-> callstack)[i ]); //printf("
-%s\n", realname);
[align=left] free(realname );[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
[align=left] char* p = strchr( strings[i ], ')');[/align]
[align=left] memcpy(output , strings[ i], p-strings [i]+2);[/align]
[align=left] }[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
[align=left] char* p = strchr( strings[i ], ')');[/align]
[align=left] memcpy(output , strings[ i], p-strings [i]+2);[/align]
[align=left] }[/align]

[align=left] FILE * fp ;[/align]
char module [1024]
= {0};
memset(module ,
0, 1024);
[align=left] char* pm = strchr( strings[i ], '(');[/align]
[align=left] memcpy(module , strings[ i], pm -strings[ i]);[/align]

[align=left] if (strstr (module, ".so"))[/align]
[align=left] {[/align]
[align=left] __pid_t pid = getpid();[/align]
sprintf(temp , "grep
%s /proc/%d/maps", module, pid );
[align=left] ///[/align]
[align=left] /// get library base-map-address[/align]
[align=left] ///[/align]
[align=left] fp = popen (temp, "r");[/align]
[align=left] if (fp )[/align]
[align=left] {[/align]
[align=left] char baseaddr [64];[/align]
[align=left] unsigned long long base;[/align]
[align=left] [/align]
fgets(temp , sizeof( temp)-1, fp ); //printf("memmap:
%s\n", temp);
[align=left] sscanf(temp , "%[^-]", baseaddr);[/align]
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));
[align=left] }[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
sprintf(temp , "addr2line
-e %s %p", module, ((void **)it-> first->callstack )[i]);
[align=left] }[/align]
[align=left] ////[/align]
[align=left] //// get source file name and line number[/align]
[align=left] ////[/align]
fp = popen (temp, "r"); //printf("cmdline:
%s\n", temp);
[align=left] if (fp )[/align]
[align=left] {[/align]
fgets(temp , sizeof( temp)-1, fp ); //printf("
-%s\n", temp);

[align=left] strcat(output , temp);[/align]
printf("
-> %s" , output);
[align=left] pclose(fp );[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
printf("
-> %s\n" , output);
[align=left] }[/align]
[align=left] }[/align]

[align=left] free(strings );[/align]
[align=left] strings = NULL ;[/align]
[align=left] }[/align]
[align=left] }[/align]

[align=left] __malloc_hook = my_malloc_hook ;[/align]
[align=left] __free_hook = my_free_hook ;[/align]

[align=left] gLock.unlock ();[/align]
[align=left]}[/align]

[align=left]//////////////////////////////////////////////////////////////////////////[/align]

[align=left]CMutexLock::CMutexLock ()[/align]
[align=left]{[/align]
[align=left] pthread_mutexattr_t m_attr ;[/align]
[align=left] pthread_mutexattr_init(&m_attr );[/align]
[align=left] pthread_mutexattr_settype(&m_attr , PTHREAD_MUTEX_RECURSIVE);[/align]

if (0 != pthread_mutex_init (&m_mutex ,
& m_attr))
[align=left] {[/align]
printf("c_lock::c_lock
pthread_mutex_init error<%d>.\n" , errno);
[align=left] assert(0);[/align]
[align=left] }[/align]

[align=left] pthread_mutexattr_destroy(&m_attr );[/align]
[align=left]}[/align]

[align=left]CMutexLock::~CMutexLock ()[/align]
[align=left]{[/align]
[align=left] if(0 != pthread_mutex_destroy (&m_mutex))[/align]
[align=left] {[/align]
printf("c_lock::~c_lock
pthread_mutex_destroy error<%d>.\n" , errno);
[align=left] assert(0);[/align]
[align=left] }[/align]
[align=left]}[/align]

[align=left]void[/align]
[align=left]CMutexLock::lock ()[/align]
[align=left]{[/align]

[align=left] if(0 != pthread_mutex_lock (&m_mutex))[/align]
[align=left] {[/align]
assert("c_lock::lock
pthread_mutex_lock " && 0);
[align=left] }[/align]
[align=left]}[/align]

[align=left]void[/align]
[align=left]CMutexLock::unlock ()[/align]
[align=left]{[/align]
int iRet =
0;

[align=left] if(0 != (iRet = pthread_mutex_unlock(& m_mutex)))[/align]
[align=left] {[/align]
printf("c_lock::unlock
pthread_mutex_unlock ret<%d> error<%d>.\n", iRet, errno );
[align=left] assert(0);[/align]
[align=left] }[/align]
[align=left]}[/align]

[align=left]示例代码:[/align]
[align=left]leakmom.h[/align]

[align=left]////////////////////////////////////////////////////////////////////////[/align]
[align=left]//[/align]
[align=left]// The Executable file MUST be linked with parameter '-rdynamic' !!![/align]
[align=left]//[/align]
[align=left]////////////////////////////////////////////////////////////////////////[/align]

[align=left]#pragma once[/align]
[align=left]#include <string.h>[/align]
[align=left]#include <pthread.h>[/align]
[align=left] [/align]
[align=left]//[/align]
[align=left]class LmCallStack[/align]
[align=left]{[/align]
[align=left]public:[/align]
char* callstack ; //
pointer to buffer recording callstack addresses
int size ; //
count of call stacks
[align=left] static const int MAX_STACK_LAYERS;[/align]
[align=left]public:[/align]
[align=left] LmCallStack(void * csk= NULL, int s=0)[/align]
[align=left] {[/align]
[align=left] if (csk )[/align]
[align=left] {[/align]
[align=left] callstack = new char[ s*sizeof (void*)];[/align]
[align=left] memcpy(callstack , csk, s*sizeof (void*));[/align]
[align=left] }[/align]
[align=left] else[/align]
[align=left] {[/align]
callstack =
(char *)csk;
[align=left] }[/align]
[align=left] size = s ;[/align]
[align=left] }[/align]

[align=left] ~ LmCallStack()[/align]
[align=left] {[/align]
[align=left] if (callstack )[/align]
[align=left] {[/align]
[align=left] delete[] callstack ;[/align]
[align=left] }[/align]
[align=left] callstack = NULL ;[/align]
size =
0;
[align=left] }[/align]

[align=left][/align]
};

[align=left]class __comp[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left] __comp(){};[/align]
bool operator ()
(const LmCallStack* first , const LmCallStack* second)
[align=left] {[/align]
return ((first ->size < second->size )
||
[align=left] ( first->size == second-> size &&[/align]
memcmp(first ->callstack, second->callstack , sizeof( void*)*first ->size)
< 0)
[align=left] );[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]struct _PtrInfo[/align]
[align=left]{[/align]
[align=left] _PtrInfo(const LmCallStack* c=NULL , long s=0)[/align]
[align=left] {[/align]
[align=left] csk = c ;[/align]
[align=left] size = s ;[/align]
[align=left] }[/align]
[align=left] const LmCallStack * csk;[/align]
[align=left] long size ;[/align]
[align=left]};[/align]

[align=left]struct _AllocInfo[/align]
[align=left]{[/align]
[align=left] _AllocInfo(long s=0, int a =0, int f=0)[/align]
[align=left] {[/align]
[align=left] size=s ;[/align]
[align=left] alloc=a ;[/align]
[align=left] free=f ;[/align]
[align=left] }[/align]
[align=left] //[/align]
[align=left] long size ;[/align]
[align=left] int alloc ;[/align]
[align=left] int free ;[/align]
[align=left]};[/align]

[align=left]class CMutexLock[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left] CMutexLock();[/align]
[align=left] ~ CMutexLock();[/align]

[align=left]public:[/align]
[align=left] void lock ();[/align]
[align=left] void unlock ();[/align]

[align=left]private:[/align]
[align=left] pthread_mutex_t m_mutex ;[/align]
[align=left]};[/align]

[align=left]//[/align]
[align=left]void RecordPtr ( void* ptr, size_t size);[/align]
[align=left]void RemovePtr (void* ptr);[/align]
[align=left]void Report ();[/align]

最后给出测试代码作为使用范例:

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: