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

尽管-rdynamic选项使得可读性大大提高,但仍然不太好根据二进制地址定位到某行泄露代码;

这时使用objdump  -S  exefile 即可)


在WINDOWS下检测内存泄露比较容易,毕竟MSDN强大,方法多多;有兴趣可以查看VLD源代码;

  前几天无意中从MAN文档中查到linux可以给malloc挂钩子;于是写下了如下检测内存泄露的代码;

已存在的程序代码只需要与linux_vld.o文件一起编译链接即可,退出时会打印内存泄露状态;


对于下面文件,g++ -c linux_vld.cc得到o文件即可;

  1. #ifndef LINUX_VLD_H   
  2. #define LINUX_VLD_H   
  3.   
  4. #include <stdlib.h>   
  5. #include <malloc.h>   
  6.   
  7.   
  8. #endif  
#ifndef LINUX_VLD_H
#define LINUX_VLD_H

#include <stdlib.h>
#include <malloc.h>


#endif


  1. #include <map>   
  2. #include <vector>   
  3. #include <string>   
  4. #include <execinfo.h>   
  5. #include <cxxabi.h>   
  6.   
  7. #include "linux_vld.h"   
  8.   
  9. static void *(*old_malloc_hook)(size_tconst void*);  
  10. static void (*old_free_hook)(void*, const void*);  
  11. static void *(*old_realloc_hook)(void*, size_tconst void*);  
  12.   
  13. static void ReportMemoryLeak(void);  
  14. static void MyHookInit(void);  
  15. static void MyHookUnInit(void);  
  16.   
  17. static void* MyMallocHook(size_tconst void* );  
  18. static void* MyReallocHook(void* , size_t , const void* );  
  19. static void  MyFreeHook(void* , const void* );  
  20.   
  21. typedef struct  
  22. {  
  23.     std::size_t  size;  
  24.     std::vector<std::string>  stackinfo;  
  25. } NODE;  
  26.   
  27. static  std::map<void*, NODE>   s_ptrContainer;  
  28. typedef std::map<void*, NODE>::iterator ITER;  
  29.   
  30. namespace  
  31. {  
  32.   
  33.     class HookGuard  
  34.     {  
  35.     private:  
  36.         static HookGuard sm_guard;  
  37.         HookGuard();  
  38.   
  39.     public:  
  40.         ~HookGuard();  
  41.     };  
  42.   
  43.     // Hook init in constructor   
  44.     HookGuard   HookGuard::sm_guard;  
  45.   
  46.     HookGuard::HookGuard()  
  47.     {  
  48.         __malloc_initialize_hook = &MyHookInit;  
  49.         atexit(&ReportMemoryLeak);  
  50.     }  
  51.   
  52.     HookGuard::~HookGuard()   
  53.     {  
  54.         MyHookUnInit();  
  55.     }  
  56. // end namespace   
  57.   
  58. static void* MyMallocHook(size_t size, const void* caller)  
  59. {  
  60.     MyHookUnInit();  
  61.   
  62.     void* pResult = ::malloc(size);  
  63.   
  64.     if (NULL != pResult)  
  65.     {  
  66.         NODE    node;  
  67.         void*   arr[32];  
  68.         int     nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));  
  69.         char** strings = ::backtrace_symbols(arr, nFrames);  
  70.         if (NULL != strings)  
  71.         {  
  72.             node.stackinfo.reserve(nFrames);  
  73.             for (int i = 0; i < nFrames; ++ i)  
  74.             {  
  75.                 node.stackinfo.push_back(strings[i]);  
  76.             }  
  77.             ::free(strings);  
  78.         }  
  79.   
  80.         node.size = size;  
  81.         s_ptrContainer[pResult] = node;  
  82.     }  
  83.   
  84.     MyHookInit();  
  85.   
  86.     return  pResult;  
  87. }  
  88.   
  89. static void* MyReallocHook(void* ptr, size_t size, const void* caller)  
  90. {  
  91.     MyHookUnInit();  
  92.   
  93.     const bool isFree(ptr != NULL && size == 0);  
  94.   
  95.     void* pResult = ::realloc(ptr, size);  
  96.   
  97.     if (NULL != pResult)  
  98.     {  
  99.         NODE    node;  
  100.         void*   arr[32];  
  101.         int     nFrames= ::backtrace(arr, sizeof(arr)/sizeof(arr[0]));  
  102.         char** strings = ::backtrace_symbols(arr, nFrames);  
  103.         if (NULL != strings)  
  104.         {  
  105.             node.stackinfo.reserve(nFrames);  
  106.             for (int i = 0; i < nFrames; ++ i)  
  107.             {  
  108.                 node.stackinfo.push_back(strings[i]);  
  109.             }  
  110.             ::free(strings);  
  111.         }  
  112.   
  113.         node.size = size;  
  114.         s_ptrContainer[pResult] = node;  
  115.     }  
  116.     else if (isFree)   
  117.     {  
  118.         ITER  it = s_ptrContainer.find(ptr);  
  119.         if (it != s_ptrContainer.end())  
  120.         {  
  121.             s_ptrContainer.erase(ptr);  
  122.         }  
  123.     }  
  124.       
  125.     MyHookInit();  
  126.   
  127.     return  pResult;  
  128. }  
  129.   
  130. static void MyFreeHook(void* ptr, const void* caller)  
  131. {  
  132.     if (NULL == ptr)  
  133.         return ;  
  134.   
  135.     MyHookUnInit();  
  136.   
  137.     ITER  it = s_ptrContainer.find(ptr);  
  138.     if (it != s_ptrContainer.end())  
  139.     {  
  140.         s_ptrContainer.erase(ptr);  
  141.     }  
  142.   
  143.     ::free(ptr);  
  144.   
  145.     MyHookInit();  
  146. }  
  147.   
  148. static void MyHookInit(void)  
  149. {  
  150.     old_malloc_hook = __malloc_hook;  
  151.     old_realloc_hook = __realloc_hook;  
  152.     old_free_hook = __free_hook;  
  153.   
  154.     __malloc_hook = &MyMallocHook;  
  155.     __realloc_hook = &MyReallocHook;  
  156.     __free_hook = &MyFreeHook;  
  157. }  
  158.   
  159. static void MyHookUnInit(void)  
  160. {  
  161.     __malloc_hook = old_malloc_hook;  
  162.     __realloc_hook = old_realloc_hook;  
  163.     __free_hook   = old_free_hook;  
  164. }  
  165.   
  166.   
  167. / TEST   
  168. static void  ReportMemoryLeak(void)  
  169. {  
  170.     if (s_ptrContainer.empty())  
  171.     {  
  172.         printf("DEBUG No Memory leak\n");  
  173.         return;  
  174.     }  
  175.   
  176.     ITER  it(s_ptrContainer.begin());  
  177.     while (it != s_ptrContainer.end())  
  178.     {  
  179.         printf("================================================\n");  
  180.         printf("DEBUG Memory leak at %p, size =[%lu]bytes\n", it->first, it->second.size);  
  181.         std::vector<std::string>::const_iterator  stackiter(it->second.stackinfo.begin());  
  182.         std::vector<std::string>::const_iterator  end(it->second.stackinfo.end());  
  183.         int   frameNo = 0;  
  184.         while (stackiter != end)  
  185.         {  
  186.             std::string  mangleName = (*stackiter).c_str();  
  187.             std::size_t start  = mangleName.find('(');  
  188.             std::size_t end    = mangleName.find('+', start);  
  189.             if (std::string::npos == start || std::string::npos == end)  
  190.             {  
  191.                 printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());  
  192.             }  
  193.             else  
  194.             {  
  195.                 ++ start;  
  196.                 int status = 0;  
  197.                 char* name = abi::__cxa_demangle(mangleName.substr(start, end - start).c_str(), NULL, 0, &status);  
  198.                 if (0 == status)  
  199.                 {  
  200.                     printf("Frame[%d]: %s\n", frameNo, name);  
  201.                     ::free(name);  
  202.                 }  
  203.                 else  
  204.                 {  
  205.                     printf("Frame[%d]: %s\n", frameNo, (*stackiter).c_str());  
  206.                 }  
  207.             }  
  208.   
  209.             ++ frameNo;  
  210.             ++ stackiter;  
  211.         }  
  212.   
  213.         ++ it;  
  214.     }  
  215. }  
#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

  1. #include <stdlib.h>     
  2.     
  3. void* TestMalloc()    
  4. {    
  5.     return ::malloc(12);    
  6. }    
  7.     
  8. void* TestRealloc(void* ptr)    
  9. {    
  10.     return ::realloc(ptr, 128);    
  11. }    
  12.     
  13.     
  14. int main(int ac, char* av[])    
  15. {    
  16.     void* ptr = TestMalloc();    
  17.     ::free(ptr);    
  18.     ptr = TestMalloc();    
  19.     ptr = NULL;    
  20.     ptr = TestRealloc(ptr);    
  21. }    
    #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);  
    }  


程序退出时输出如下

  1. ================================================    
  2. DEBUG Memory leak at 0x50c010, size =[12]bytes    
  3. Frame[0]: ./a.out [0x405ee5]    
  4. Frame[1]: TestMalloc()    
  5. Frame[2]: ./a.out(main+0x26) [0x4056fa]    
  6. Frame[3]: /lib64/libc.so.6(__libc_start_main+0xf4) [0x2ab4fc643154]    
  7. Frame[4]: ./a.out(__gxx_personality_v0+0x69) [0x405619]    
  8. ================================================    
  9. DEBUG Memory leak at 0x50c160, size =[128]bytes    
  10. Frame[0]: ./a.out [0x405d09]    
  11. Frame[1]: TestRealloc(void*)    
  12. Frame[2]: ./a.out(main+0x3b) [0x40570f]    
  13. Frame[3]: /lib64/libc.so.6(__libc_start_main+0xf4) [0x2ab4fc643154]    
  14. 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、付费专栏及课程。

余额充值