尽管-rdynamic选项使得可读性大大提高,但仍然不太好根据二进制地址定位到某行泄露代码;
这时使用objdump -S exefile 即可)
在WINDOWS下检测内存泄露比较容易,毕竟MSDN强大,方法多多;有兴趣可以查看VLD源代码;
前几天无意中从MAN文档中查到linux可以给malloc挂钩子;于是写下了如下检测内存泄露的代码;
已存在的程序代码只需要与linux_vld.o文件一起编译链接即可,退出时会打印内存泄露状态;
对于下面文件,g++ -c linux_vld.cc得到o文件即可;
- #ifndef LINUX_VLD_H
- #define LINUX_VLD_H
- #include <stdlib.h>
- #include <malloc.h>
- #endif
#ifndef LINUX_VLD_H
#define LINUX_VLD_H
#include <stdlib.h>
#include <malloc.h>
#endif
- #include <map>
- #include <vector>
- #include <string>
- #include <execinfo.h>
- #include <cxxabi.h>
- #include "linux_vld.h"
- static void *(*old_malloc_hook)(size_t, const void*);
- static void (*old_free_hook)(void*, const void*);
- static void *(*old_realloc_hook)(void*, size_t, const void*);
- static void ReportMemoryLeak(void);
- static void MyHookInit(void);
- static void MyHookUnInit(void);
- static void* MyMallocHook(size_t, const void* );
- static void* MyReallocHook(void* , size_t , const void* );
- static void MyFreeHook(void* , const void* );
- typedef struct
- {
- std::size_t size;
- std::vector<std::string> stackinfo;
- } NODE;
- static std::map<void*, NODE> s_ptrContainer;
- typedef std::map<void*, NODE>::iterator ITER;
- namespace
- {
- class HookGuard
- {
- private:
- static HookGuard sm_guard;
- HookGuard();
- public:
- ~HookGuard();
- };
- // Hook init in constructor
- HookGuard HookGuard::sm_guard;
- HookGuard::HookGuard()
- {
- __malloc_initialize_hook = &MyHookInit;
- atexit(&ReportMemoryLeak);
- }
- HookGuard::~HookGuard()
- {
- MyHookUnInit();
- }
- } // end namespace
- static void* MyMallocHook(size_t size, const void* caller)
- {
- MyHookUnInit();
- void* pResult = ::malloc(size);
- if (NULL != pResult)
- {
- NODE node;
- void* arr[32];
- int nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));
- char** strings = ::backtrace_symbols(arr, nFrames);
- if (NULL != strings)
- {
- node.stackinfo.reserve(nFrames);
- for (int i = 0; i < nFrames; ++ i)
- {
- node.stackinfo.push_back(strings[i]);
- }
- ::free(strings);
- }
- node.size = size;
- s_ptrContainer[pResult] = node;
- }
- MyHookInit();
- return pResult;
- }
- static void* MyReallocHook(void* ptr, size_t size, const void* caller)
- {
- MyHookUnInit();
- const bool isFree(ptr != NULL && size == 0);
- void* pResult = ::realloc(ptr, size);
- if (NULL != pResult)
- {
- NODE node;
- void* arr[32];
- int nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));
- char** strings = ::backtrace_symbols(arr, nFrames);
- if (NULL != strings)
- {
- node.stackinfo.reserve(nFrames);
- for (int i = 0; i < nFrames; ++ i)
- {
- node.stackinfo.push_back(strings[i]);
- }
- ::free(strings);
- }
- node.size = size;
- s_ptrContainer[pResult] = node;
- }
- else if (isFree)
- {
- ITER it = s_ptrContainer.find(ptr);
- if (it != s_ptrContainer.end())
- {
- s_ptrContainer.erase(ptr);
- }
- }
- MyHookInit();
- return pResult;
- }
- static void MyFreeHook(void* ptr, const void* caller)
- {
- if (NULL == ptr)
- return ;
- MyHookUnInit();
- ITER it = s_ptrContainer.find(ptr);
- if (it != s_ptrContainer.end())
- {
- s_ptrContainer.erase(ptr);
- }
- ::free(ptr);
- MyHookInit();
- }
- static void MyHookInit(void)
- {
- old_malloc_hook = __malloc_hook;
- old_realloc_hook = __realloc_hook;
- old_free_hook = __free_hook;
- __malloc_hook = &MyMallocHook;
- __realloc_hook = &MyReallocHook;
- __free_hook = &MyFreeHook;
- }
- static void MyHookUnInit(void)
- {
- __malloc_hook = old_malloc_hook;
- __realloc_hook = old_realloc_hook;
- __free_hook = old_free_hook;
- }
- / TEST
- static void ReportMemoryLeak(void)
- {
- if (s_ptrContainer.empty())
- {
- printf("DEBUG No Memory leak\n");
- return;
- }
- ITER it(s_ptrContainer.begin());
- while (it != s_ptrContainer.end())
- {
- printf("================================================\n");
- printf("DEBUG Memory leak at %p, size =[%lu]bytes\n", it->first, it->second.size);
- std::vector<std::string>::const_iterator stackiter(it->second.stackinfo.begin());
- std::vector<std::string>::const_iterator end(it->second.stackinfo.end());
- int frameNo = 0;
- while (stackiter != end)
- {
- std::string mangleName = (*stackiter).c_str();
- std::size_t start = mangleName.find('(');
- std::size_t end = mangleName.find('+', start);
- if (std::string::npos == start || std::string::npos == end)
- {
- printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());
- }
- else
- {
- ++ start;
- int status = 0;
- char* name = abi::__cxa_demangle(mangleName.substr(start, end - start).c_str(), NULL, 0, &status);
- if (0 == status)
- {
- printf("Frame[%d]: %s\n", frameNo, name);
- ::free(name);
- }
- else
- {
- printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());
- }
- }
- ++ frameNo;
- ++ stackiter;
- }
- ++ it;
- }
- }
#include <map>
#include <vector>
#include <string>
#include <execinfo.h>
#include <cxxabi.h>
#include "linux_vld.h"
static void *(*old_malloc_hook)(size_t, const void*);
static void (*old_free_hook)(void*, const void*);
static void *(*old_realloc_hook)(void*, size_t, const void*);
static void ReportMemoryLeak(void);
static void MyHookInit(void);
static void MyHookUnInit(void);
static void* MyMallocHook(size_t, const void* );
static void* MyReallocHook(void* , size_t , const void* );
static void MyFreeHook(void* , const void* );
typedef struct
{
std::size_t size;
std::vector<std::string> stackinfo;
} NODE;
static std::map<void*, NODE> s_ptrContainer;
typedef std::map<void*, NODE>::iterator ITER;
namespace
{
class HookGuard
{
private:
static HookGuard sm_guard;
HookGuard();
public:
~HookGuard();
};
// Hook init in constructor
HookGuard HookGuard::sm_guard;
HookGuard::HookGuard()
{
__malloc_initialize_hook = &MyHookInit;
atexit(&ReportMemoryLeak);
}
HookGuard::~HookGuard()
{
MyHookUnInit();
}
} // end namespace
static void* MyMallocHook(size_t size, const void* caller)
{
MyHookUnInit();
void* pResult = ::malloc(size);
if (NULL != pResult)
{
NODE node;
void* arr[32];
int nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));
char** strings = ::backtrace_symbols(arr, nFrames);
if (NULL != strings)
{
node.stackinfo.reserve(nFrames);
for (int i = 0; i < nFrames; ++ i)
{
node.stackinfo.push_back(strings[i]);
}
::free(strings);
}
node.size = size;
s_ptrContainer[pResult] = node;
}
MyHookInit();
return pResult;
}
static void* MyReallocHook(void* ptr, size_t size, const void* caller)
{
MyHookUnInit();
const bool isFree(ptr != NULL && size == 0);
void* pResult = ::realloc(ptr, size);
if (NULL != pResult)
{
NODE node;
void* arr[32];
int nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));
char** strings = ::backtrace_symbols(arr, nFrames);
if (NULL != strings)
{
node.stackinfo.reserve(nFrames);
for (int i = 0; i < nFrames; ++ i)
{
node.stackinfo.push_back(strings[i]);
}
::free(strings);
}
node.size = size;
s_ptrContainer[pResult] = node;
}
else if (isFree)
{
ITER it = s_ptrContainer.find(ptr);
if (it != s_ptrContainer.end())
{
s_ptrContainer.erase(ptr);
}
}
MyHookInit();
return pResult;
}
static void MyFreeHook(void* ptr, const void* caller)
{
if (NULL == ptr)
return ;
MyHookUnInit();
ITER it = s_ptrContainer.find(ptr);
if (it != s_ptrContainer.end())
{
s_ptrContainer.erase(ptr);
}
::free(ptr);
MyHookInit();
}
static void MyHookInit(void)
{
old_malloc_hook = __malloc_hook;
old_realloc_hook = __realloc_hook;
old_free_hook = __free_hook;
__malloc_hook = &MyMallocHook;
__realloc_hook = &MyReallocHook;
__free_hook = &MyFreeHook;
}
static void MyHookUnInit(void)
{
__malloc_hook = old_malloc_hook;
__realloc_hook = old_realloc_hook;
__free_hook = old_free_hook;
}
/ TEST
static void ReportMemoryLeak(void)
{
if (s_ptrContainer.empty())
{
printf("DEBUG No Memory leak\n");
return;
}
ITER it(s_ptrContainer.begin());
while (it != s_ptrContainer.end())
{
printf("================================================\n");
printf("DEBUG Memory leak at %p, size =[%lu]bytes\n", it->first, it->second.size);
std::vector<std::string>::const_iterator stackiter(it->second.stackinfo.begin());
std::vector<std::string>::const_iterator end(it->second.stackinfo.end());
int frameNo = 0;
while (stackiter != end)
{
std::string mangleName = (*stackiter).c_str();
std::size_t start = mangleName.find('(');
std::size_t end = mangleName.find('+', start);
if (std::string::npos == start || std::string::npos == end)
{
printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());
}
else
{
++ start;
int status = 0;
char* name = abi::__cxa_demangle(mangleName.substr(start, end - start).c_str(), NULL, 0, &status);
if (0 == status)
{
printf("Frame[%d]: %s\n", frameNo, name);
::free(name);
}
else
{
printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());
}
}
++ frameNo;
++ stackiter;
}
++ it;
}
}
g++ -rdynamic file.cc linux_vld.o
- #include <stdlib.h>
- void* TestMalloc()
- {
- return ::malloc(12);
- }
- void* TestRealloc(void* ptr)
- {
- return ::realloc(ptr, 128);
- }
- int main(int ac, char* av[])
- {
- void* ptr = TestMalloc();
- ::free(ptr);
- ptr = TestMalloc();
- ptr = NULL;
- ptr = TestRealloc(ptr);
- }
#include <stdlib.h>
void* TestMalloc()
{
return ::malloc(12);
}
void* TestRealloc(void* ptr)
{
return ::realloc(ptr, 128);
}
int main(int ac, char* av[])
{
void* ptr = TestMalloc();
::free(ptr);
ptr = TestMalloc();
ptr = NULL;
ptr = TestRealloc(ptr);
}
程序退出时输出如下
- ================================================
- DEBUG Memory leak at 0x50c010, size =[12]bytes
- Frame[0]: ./a.out [0x405ee5]
- Frame[1]: TestMalloc()
- Frame[2]: ./a.out(main+0x26) [0x4056fa]
- Frame[3]: /lib64/libc.so.6(__libc_start_main+0xf4) [0x2ab4fc643154]
- Frame[4]: ./a.out(__gxx_personality_v0+0x69) [0x405619]
- ================================================
- DEBUG Memory leak at 0x50c160, size =[128]bytes
- Frame[0]: ./a.out [0x405d09]
- Frame[1]: TestRealloc(void*)
- Frame[2]: ./a.out(main+0x3b) [0x40570f]
- Frame[3]: /lib64/libc.so.6(__libc_start_main+0xf4) [0x2ab4fc643154]
- Frame[4]: ./a.out(__gxx_personality_v0+0x69) [0x405619]