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


      Windows 程序内存泄漏检测是一项十分重要的工作,基于 GUI 的应用程序通常在调试结束时也有内存泄漏报告,但这个报告的信息不全面,不能定位到产生泄漏的具体行号。其实自己实现一个内存泄漏检测工具是一件非常简单的事情,但看过网上写的很多例子,普遍存在两种问题:

      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() 被调用之时还没释放,则可能存在误报现象。这是一个老大难问题了,目前还没有完美的解决方法。

    CodeProject

  • 相关阅读:
    方维P2P  二次开发
    Array 数组去重 总结10方法(7)
    PHP  OOP学习总结
    [转载]js:数组里面获取键名和键值
    Array对象的方法实现(6)----Array.prototype.indexOf(实现常规参数的功能)
    在Apache服务器上启用GZip压缩静态内容的方法
    PHP 程序授权验证开发思路
    【转】zend studio中ctrl+鼠标左键无法转到类或函数定义文件的解决方法
    公钥私钥,HTTPS,CA证书机构,单向和双向认证
    Array对象的方法实现(5)----Array.prototype.includes(实现常规参数的功能)
  • 原文地址:https://www.cnblogs.com/ldcsaa/p/2348160.html
Copyright © 2020-2023  润新知