基于源码内存泄漏的快速调试方法

可重现内存泄漏调试

1.    调试原理

VS内存泄漏调试基于CRT库的支持,其原理是调用内存分配的另外一个实现实例,从而记录内存分配时的程序信息(包括文件名和行号等)。在程序运行结束时,调用CRT的内存检测函数,定位到第几次分配的内存没有释放。之后设置内存分配函数在相应的次数之后中断,观察函数调用堆栈,从而确定用户代码分配内存的地方。

2.    情景模拟

 

(1).   Debug版本时,调用new的另外一个实现实例,如下所示:

#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

这个实现实例是CRT的内存调试功能,其函数原型及定义如下所示:

void *__CRTDECL operator new(
        size_t cb,
        
int nBlockUse,
        
const char * szFileName,
        
int nLine
        )
        _THROW1(_STD bad_alloc)
{
    
/* _nh_malloc_dbg already calls _heap_alloc_dbg in a loop and calls _callnewh
       if the allocation fails. If _callnewh returns (very likely because no
       new handlers have been installed by the user), _nh_malloc_dbg returns NULL.
     */

    
void *res = _nh_malloc_dbg( cb, 1, nBlockUse, szFileName, nLine );

    RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));

    
/* if the allocation fails, we throw std::bad_alloc */
    
if (res == 0)
    {
        
static const std::bad_alloc nomem;
        _RAISE(nomem);
    }

    
return res;
}

 

(2).   模拟内存泄漏

char* p = new char[10];

 

(3).   main函数退出之前调用CRT的内存分析函数

_CrtDumpMemoryLeaks();

 

(4).   程序运行完毕后,“输出(Output)窗口”会显示内存分析报告,如下所示

其中花括号中的数字{N}代表的是此块内存是系统第N次分配的,如果我们想知道是在程序源文件具体的哪一行分配的,只需要在系统第N次分配时下断点,然后根据函数调用堆栈就可以定位到用户代码调用new分配内存的那一行。

3.    泄漏分析

(1).   我们获得第N次分配的内存没有被释放后,可以通过如下函数,让程序在第N次分配时暂停下来。

_CrtSetBreakAlloc(N);

 

(2).   针对本问的例子,我们可以设置N119,程序在分配到第119次内粗时会触发一个断点。

此时break程序执行,观察函数堆栈,如下所示:

根据函数堆栈,就可以定位到用户代码调用内存分配的地方了。

 

4.    优缺点

优点:

ü  易用性:借助与VS辅助平台,分析方便

缺点:

  入侵性:需要在程序代码中添加CRT的辅助函数

  局限性:只能分析泄漏内存的分配序号是固定的内存泄漏,并且需要源代码和Debug程序版本

 

Windbg+CRT内存泄漏调试

1.    调试原理

在调试版本,用CRT辅助函数,找到泄漏内存块的地址,之后通过结合Windbg!heap命令对进程堆的内存进行跟踪和分析,从而找到内存泄漏的地方。

2.    情景模拟

模拟内存泄漏的代码如下所示:

// MemleakTest.cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

 

 

#ifdef _DEBUG

#define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)

#else

#define DEBUG_CLIENTBLOCK

#endif

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

#ifdef _DEBUG

#define new DEBUG_CLIENTBLOCK

#endif

#include <process.h>

 

 

int _tmain(int argc, _TCHAR* argv[])

{

         _CrtSetBreakAlloc(119);

 

         char* p = new char[10];

        

         _CrtDumpMemoryLeaks();

         system("pause");

         return 0;

}

 

3.    泄漏分析

(1).     VS以调试的方式运行上述代码,执行到system(“pause”)时,观察输出窗口的内容,如下所示:

 

其中泄漏的内存块的地址为0x03196FF0

(2).     VS剥离当前调试进程,并用Windbg附加到剥离的进程,设置好程序的符号路径后,用!heap命令进行此块内存的调用堆栈分析。

根据打印出来的函数堆栈,我们可以定位到分配此块内存时用户程序函数中的偏移量。

(3).     ln命令查找此偏移量所在的源文件及行号

查看源代码,发现此处正好是我们分配此块内存的地方。

 

4.    优缺点

优点:

ü  易用性:借助CRT打印内存泄漏地址,用Windbg直接可以分析出泄漏内存的分配地址。

缺点:

  入侵性:需要在程序代码中添加CRT的辅助函数

  局限性:需要源代码和Debug程序版本

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值