不改动已存在代码,程序退出时检测内存泄露

(2012.05.19补充:

尽管-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


#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;
    }
}





下面是一个测试文件file.cc,编译指令如下

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);  
    }  


程序退出时输出如下

================================================  
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]  


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值