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