当前位置:软件学习 > 其它软件 >>

基于crt debug实现的Windows程序内存泄漏检测工具

Windows 程序内存泄漏检测是一项十分重要的工作,基于 GUI 的应用程序通常在调试结束时也有内存泄漏报告,但这个报告的信息不全面,不能定位到产生泄漏的具体行号。其实自己实现一个内存泄漏检测工具是一件非常简单的事情,但看过网上写的很多例子,普遍存在两种问题:
1. 
1. 要么考虑不周全,一种环境下能用,而在另外一种环境下却不能很好工作,或者漏洞报告的输出方式不合理。
2. 要么过于保守,例如:完全没有必要在 _malloc_dbg() 和 _free_dbg() 的调用前后用 CriticalSection 进行保护(跟踪一下多线程环境下 new 和 malloc 的代码就会明白)。
  内存检测主要用到以下几个 API,这些 API 能跟踪 new 和 malloc 系列方法申请的内存,具体说明参考帮助文档:
struct _CrtMemState;

_CrtSetDbgFlag();
_CrtMemCheckpoint();
_CrtMemCheckpoint();
_CrtMemDifference();
_CrtMemDumpStatistics();
_malloc_dbg();
_free_dbg();
 
•   头文件:win32_crtdbg.h
#pragma once

#if defined _DEBUG && defined _DETECT_MEMORY_LEAK

#ifdef new
    #undef new
#endif

#ifdef delete
    #undef delete
#endif

#ifndef _CRTDBG_MAP_ALLOC
    #define _CRTDBG_MAP_ALLOC
#endif

#include <crtdbg.h>

namespace __dbg_impl
{
    class CDebugEnv
    {
    public:
        CDebugEnv()
        {
            ::_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
            ::_CrtMemCheckpoint(&s1);
        }

        ~CDebugEnv()
        {
            ::_CrtMemCheckpoint(&s2);

            if (::_CrtMemDifference( &s3, &s1, &s2))
            {
                TRACE0("!! Memory stats !!\n");
                TRACE0("----------------------------------------\n");
                ::_CrtMemDumpStatistics(&s3);
                TRACE0("----------------------------------------\n");
            }
        }

    private:
        _CrtMemState s1, s2, s3;
    };

    static __dbg_impl::CDebugEnv __dbgEnv;
}

inline void* __cdecl operator new(size_t nSize, const char* lpszFileName, int nLine)
{
    return ::_malloc_dbg(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
}

inline void* __cdecl operator new[](size_t nSize, const char* lpszFileName, int nLine)
{
    return operator new(nSize, lpszFileName, nLine);
}

inline void* __cdecl operator new(size_t nSize)
{
    return operator new(nSize, __FILE__, __LINE__);
}

inline void* __cdecl operator new[](size_t nSize)
{
    return operator new(nSize, __FILE__, __LINE__);
}

inline void* __cdecl operator new(size_t nSize, const std::nothrow_t&)
{
    return operator new(nSize, __FILE__, __LINE__);
}

inline void* __cdecl operator new[](size_t nSize, const std::nothrow_t&)
{
    return operator new(nSize, __FILE__, __LINE__);
}

inline void __cdecl operator delete(void* p)
{
    ::_free_dbg(p, _NORMAL_BLOCK);
}

inline void __cdecl operator delete[](void* p)
{
    operator delete(p);
}

inline void __cdecl operator delete(void* p, const char* lpszFileName, int nLine)
{
    operator delete(p);
}

inline void __cdecl operator delete[](void* p, const char* lpszFileName, int nLine)
{
    operator delete(p);
}

inline void __cdecl operator delete(void *p, const std::nothrow_t&)
{
    operator delete(p);
}

inline void __cdecl operator delete[](void *p, const std::nothrow_t&)
{
    operator delete(p);
}

#define new new(__FILE__, __LINE__)

#endif // _DEBUG && defined _DETECT_MEMORY_LEAK
 
•   实现文件:win32_crtdbg.cpp
#include "stdafx.h"
#include "win32_crtdbg.h"

#if defined _DEBUG && defined _DETECT_MEMORY_LEAK

__dbg_impl::CDebugEnv __dbgEnv;

#endif // _DEBUG && defined _DETECT_MEMORY_LEAK
 
• 使用方法
1. 在 stdafx.h 或其他公共头文件中: #define_DETECT_MEMORY_LEAK,#include"win32_crtdbg.h"。
2. 删除项目工程模板中自动生成的 new 操作符重定义,通常自动生成的 cpp 文件在 DEBUG 环境下会把 new 重定义为 DEBUG_NEW。
• 存在问题
    对于某些全局变量指向的堆内存,如果 ~CDebugEnv() 被调用之时还没释放,则可能存在误报现象。这是一个老大难问题了,目前还没有完美的解决方法。

 

摘自 ~怪^_*兽~
补充:软件开发 , 其他 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,