• 基于 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

  • 相关阅读:
    JAVA语法之小结
    JAVA之经典Student问题1
    Android之动画1
    Android之屏幕测试
    Android之点击切换图片
    Android之标签选项卡
    Android简单计算器
    Javascript之相册拖动管理
    Javascript之改变盒子颜色
    CSS之照片翻转
  • 原文地址:https://www.cnblogs.com/ldcsaa/p/2348160.html
Copyright © 2020-2023  润新知