(DEBUG相关)使用调试C运行时间库(DCRT)进行检查

来源:互联网 发布:手机广告过滤软件 编辑:程序博客网 时间:2024/06/05 22:42
使用调试C运行时间库(DCRT)进行检查

8.4.1  DCRT库介绍

微软的C运行时库可以分成两大类:调试版(DCRT)和发行版(CRT),每类中又包含单线程静态库、多线程静态库和多线程动态链接库。我们需要使用的是调试版的C运行时库(DCRT),实际应用中可以根据项目具体情况选择对应的静态(动态)单线程(多线程)库。微软的Visual C++安装后带有DCRT库的源代码,可以在VC的CRT目录中找到。

在CRT目录下,有几个源文件用以调试内存越界和泄漏情况,如表8-1所示:

表8-1  调试C运行时间库的主要源代码文件 调试C运行时间库的主要源代码文件

源代码文件
源代码文件的说明
dbgdel.cpp
调试全局delete操作符的源代码
dbgheap.c
所有的调试堆的处理函数
dbghook.c
DCRT库挂钩函数源文件
dbgnew.cpp
调试new操作符的源代码
dbgrpt.c
调试信息报告函数的源代码
crtdbg.h
用户使用DCRT库需要包含的头文件
8.4.2  DCRT库的使用方法
使用DCRT库包括以下几个步骤,
第1步:定义宏_CRTDBG_MAP_ALLOC,可以在用户自己源代码文件的所有#include语句之前定义这个宏,也可以在项目设置里定义这个宏。定义了这个宏后,内存分配函数和释放函数将被重定向到调试库里的特殊版本的分配和释放函数上。
打开crtdbg.h文件可以看到,里面有以下的宏定义:
#define   malloc(s)  _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define   free(p)     _free_dbg(p, _NORMAL_BLOCK)
所以当定义了宏_CRTDBG_MAP_ALLOC后,malloc()被定向到_malloc_dbg()函数上,而free()则被定向到_free_dbg()函数上。还有其他函数被定向到了对应的调试版本的函数上。
第2步:打开DCRT库的各种功能。DCRT库的大多数功能在默认情况下是关闭的,需要使用_CrtSetDbgFlag()函数将需要使用的功能打开。_CrtSetDbgFlag()函数的使用方法可以看MSDN里的帮助。
第3步:使用调试信息报告函数来获得可读性更强的输出,例如以下代码便实现了将信息输出到标准输出上。
    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
第4步:调用DCRT库的函数来检查内存,最常用的函数有_CrtCheckMemory、_CrtMemCheckPoint、_CrtMemDifference、_CrtMemDumpStatistics、_CrtDumpMemory- Leaks等。
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <assert.h>
#define  SET_CRT_DEBUG_FIELD(a) /
            _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
void main(int argc, char *argv[])
{
    int *p1, *p2;
    // Send all reports to STDOUT
   _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
   _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
   _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
    p1 = (int *)malloc(100);
    p2 = (int *)malloc(200);
    assert(p1 && p2);
    *(int *)((char *)p1+100) = 0;
    free(p1);
    _CrtDumpMemoryLeaks();
    _CrtCheckMemory();
    SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF );
}
上面这段程序是对前文描述的知识的综合应用:首先定义了_CRTDBG_MAP_ALLOC宏;然后在main()函数中,调用信息报告函数将信息输出设置到标准输出中;接下来申请分配了两块内存;最后给程序制造了两个Bug——p1指向的内存写越界了,p2指向的内存则没有被释放。运行程序后可以看到输出如下:
memory check error at 0x00421E34 = 0x00, should be 0xFD.
memory check error at 0x00421E35 = 0x00, should be 0xFD.
memory check error at 0x00421E36 = 0x00, should be 0xFD.
memory check error at 0x00421E37 = 0x00, should be 0xFD.
DAMAGE: after Normal block (#48) at 0x00421DD0.
Detected memory leaks!
Dumping objects ->
f:/test/testmalloc.cpp(21) : {49} normal block at 0x00421CD0, 
       200 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
Detected memory leaks!
Dumping objects ->
f:/test/testmalloc.cpp(21) : {49} normal block at 0x00421CD0, 
       200 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
从输出信息中可以看出,有4个字节的内存校验未通过——发生了越界;另外源代码第21行(给p2分配内存的那行)存在内存泄漏。
前面仅仅讲述了DCRT库的基本使用方法,如果要对DCRT库进一步使用的话,可以参考MSDN帮助和阅读DCRT库的源代码文件。
另外,目前也有很多开源的程序库实现了类似DCRT库的功能,如DUMA库就是一个可以支持多种操作系统的进行内存检查的程序库。