BoundsChecker使用说明

目录

目录.... 1

一、    BoundsChecker 简介.... 2

二、    安装环境.... 2

三、    BoundsChecker两种工作模式.... 3

3.1      ActiveCheck.. 3

3.2      FinalCheck.. 3

四、    特性和优点.... 5

五、    检测示例.... 6

5.1 内存泄漏检测示例.... 6

5.2 野指针检测示例.... 8

5.3 数组越界检测示例.... 10

5.4 GDI 资源泄漏检测示例.... 12

5.5 句柄资源泄漏检测示例.... 14

5.6 死锁检测示例.... 15

5.7 MS C-Runtime Library内建的检测功能示例.... 18

六、    配置.... 19

6.1 Suppression.. 19

6.2 Filter.. 20

6.3设置.... 21

6.4 代码控制.... 22

6.5设置应用程序关联Bounds Checker.. 22

七、    注意事项.... 22

八、    总结.... 23

 

 

 

 

 

 

 

 

 

 

 

 

 

 

一、           BoundsChecker 简介

BoundsChecker是一个运行时错误检测工具,它主要定位程序在运行时期发生的各种错误。它通过驻留在 Visual C++ 开发环境内部的自动调试处理程序来加速应用程序的开发,缩短产品发布的时间。BoundsChecker 对于编程中的错误,大多数是C++中特有的提供了清晰的详细的分析。它能够检测和诊断出在静态,堆栈内存中的错误以及内存和资源泄漏问题。在运行状态下,BoundsChecker验证超过8700APIsOLE方法,包括最新的Windows APIs,ODBC, ActiveXDirectX, COM Internet APIs

BoundsChecker采用一种被称为 Code Injection的技术,来截获对分配内存和释放内存的函数的调用。简单地说,当你的程序开始运行时,BoundsCheckerDLL被自动载入进程的地址空间,然后它会修改进程中对内存分配和释放的函数调用,让这些调用首先转入它的代码,然后再执行原来的代码。BoundsChecker在做这些动作的时,无须修改被调试程序的源代码或工程配置文件,这使得使用它非常的简便、直接。

程序员在开发过程中可能会经常遇到这样的问题:调试时语法没有问题,代码也没有错误,但应用程序运行就是不正常甚至死机,其实这有可能是由于逻辑错误引起的内存溢出或资源泄露等问题,这些错误一般是不容易被检测出来的。而这类错误就是BoundsChecker错误检测范围之一。

通过对被测应用程序的操作,BoundsChecker提供清晰的、详细的程序错误分析,自动查明静态的堆栈错误及内存/资源泄露,并能够迅速的定位出错的源代码,即使在没有源代码的情况下也可检查第三方组件的错误。

BoundsChecker能检测的错误包括:

1)指针操作和内存、资源泄露错误。

比如:内存泄露;资源泄露;对指针变量的错误操作。

2)内存操作方面的错误。

比如:内存读、写溢出;使用未初始化的内存。

3API函数使用错误。

 

二、           安装环境

BoundsChecker 7.2支持的语言和主机平台包括C++, Delphi

Windows NT, Windows95/98/2000

支持Visual C++ 6.0 SP6Visual Studio .NET 2002,Visual Studio .NET 2003

安装说明:

1.首先请确定你已经卸载了旧版本的程序。

2.运行Setup目录中的文件进行安装。

3.启动安装程序,使用Setup/Crack目录中的bc72.dat文件注册程序。

4.完成即0K

5.调试方式有两种

       a. 直接启动BC.exe

       b. 集成在VC中,启动VCdebug

BoundsChecker 集成在6.0的菜单项和工具条的界面如下:

图(1)菜单项

图(2)工具条

三、           BoundsChecker两种工作模式

使用BoundsChecker对程序的运行时错误进行检测,有两种使用模式可供选择。一种模式叫做ActiveCheck,一种模式叫做FinalCheck。下面分别进行介绍。

3.1   ActiveCheck

ActiveCheckBoundsChecker提供的一种方便、快捷的错误检测模式,它能检测的错误种类有限,只包括:内存泄露错误、资源泄露错误、API函数使用错误。要想使用ActiveCheck模式来检测程序的运行时错误,只需在VC++集成开发环境中打开BoundsChecker功能,然后从调试状态运行程序即可。此时ActiveCheck会在后台自动运行,随时检测程序是否发生了错误。下面说一下具体的使用步骤。

首先,在VC++集成开发环境中打开你要对其进行测试的程序,同时保证项目处于Debug编译状态下。

其次,确保VC++集成开发环境中[BoundsChecker/Error Detection]菜单项和[BoundsChecker/Log Events]菜单项处于被选中的状态。只有这两项被选中,BoundsChecker才会在程序运行过程中发挥作用。 最后,在VC++集成开发环境中选择[Build/ Start Debug/Go]菜单命令,在Debug状态下运行程序,ActiveCheck也在后台开始运行了。

3.2   FinalCheck

FinalCheck具有BoundsChecker提供的所有检错功能。FinalCheckActiveCheck的超集,它除了能够检测出ActiveCheck能够检测出的错误,还能发现很多ActiveCheck不能检测到的错误,包括:指针操作错误、内存操作溢出、使用未初始化的内存等等,并且,对于ActiveCheck能检测出的错误,FinalCheck能够给出关于错误更详细的信息。所以,我们可以把FinalCheck认为是ActiveCheck的功能增强版。我们付出的代价是:程序的运行速度会变慢,有时甚至会变的很慢。要想在FinalCheck模式下测试程序,不能使用VC++集成开发环境提供的编译连接器来构造程序,而必须要使用BoundsChecker提供的编译连接器来编译连接程序。当BoundsChecker的编译连接器编译连接程序时,会向程序中插装一些错误检测代码,这也就是FinalCheck能够比ActiveCheck找到更多错误的原因。下面就介绍一下如何在FinalCheck模式下对程序进行测试:

1)在VC++集成开发环境中打开你所要测试的项目。

2)由于要使用BoundsChecker的编译连接器重新编译连接程序,所以我们为BoundsChecker独自构造一个文件夹。在VC++集成开发环境中,具体操作方法是:

A)点击[ Build/Configurations...]菜单命令。

B)在弹出的对话框中点击Ad按钮。在Configuration 编辑框中添入你为BoundsChecker创建的文件夹的名称,这个名称是任意的,比如我们取名为BoundChecker

C)Copy settings from组合框中选中XXX—Win32 Debug项,然后点击OK按钮,接着点击Close按钮。现在,我们已经为FinalCheck构造好了一个文件夹。

3)点击[Build/Set Active Configuration…]菜单命令,选中你刚才为BoundsChecker建的文件夹,然后点击OK按钮。这样BoundsChecker编译连接程序时生成的中间文件、可执行程序,都会被放到该文件夹下。

4)选择[BoundsChecker/Rebuild All with BoundsChecker]菜单命令,对程序重新进行编译连接,也就是在这时,BoundsChecker向被测程序的代码中加入了错误检测码。编译连接完成后,BoundsChecker会在你为BoundsChecker构造的文件夹中生成可执行文件。 FinalCheck模式下对程序进行检测的准备工作都已经做好,这时可以启动程序开始测试了,作步骤与在ActiveChecker模式下没什么区别。具体步骤如下:

确保VC++集成开发环境中[BoundsChecker/ Error Detection]菜单项和[BoundsChecker/ Log Events]菜单项处于选中状态,别外设置[BoundsChecker / Setting] Memory Tracking 选项中的Enable FinalCheckt为选中状态。

点击[ Build/Start Debug]菜单,选中“Go” 菜单项。程序开始在Debug状态下运行。按照你制定好的测试用例,对程序进行操作。

BoundsChecker 检测到了错误时,会弹出窗口向你汇报,你可以当时就进行处理,也可以等到你的操作全部完成,退出程序之后再对列出的这些错误进行分析。这完全取决于你是否 选中了[BoundsChecker/Display Error and Pause] 菜单项。

退出程序后,BoundsChecker会给出错误检测结果列表。该错误列表与ActiveChecker给出的错误列表的查看方法完全一样。只不过这个列表中所报告的信息会更多、更详细一些。

ActiveCheckerFinalCheck这两种模式,比较而言 各有长短。ActiveChecker使用方便,只需在Debug状态下直接运行程序即可,并且程序的运行速度较快,但检测的错误种类有限; FinalCheck模式下,需要使用BoundsChecker的编译连接器重新编译连接生成可执行程序,并且程序的运行速度比较慢,但检测的错误种 类、提供的错误相关信息要多于ActiveChecker。所以,何时使用何种模式,应根据当时的具体情况而定。

 

四、           特性和优点

1.资源泄漏检测:BoundsChecker 能够自动定位难以发现的内存泄漏,并监视堆栈和静态内存的状况。这样就节约了你的时间,使您能够开发出更加可靠,不出问题的应用程序。

2.Active API 检查:该特点可以减少你的调试时间,提供工业中最为全面的 Windows API 校验。这样的结果就会带来更高质量的代码,在程序发布时就不会失败。

3.IDE 集成:BoundsChecker 让你透明的调试。它提供在 C++ 中直接访问BoundsChecker 的菜单,工具条和设置,使得开发人员能够立即修复错误。

4.兼容性检查:BoundsChecker 允许你轻松的生成和发布跨Microsoft 平台的应用程序。BoundsChecker 在多平台上校验代码,然后产生一个报告指出所有与 Windows 平台兼容性相关的问题。

程序特点:

1.本地应用程序死锁检查

2.内存和资源查看器

3.COM调用报告

4.NET调用报告

5.垃圾收集(Garbage collection)通知

BoundsChecker错误检测范围主要包括:

1).指针和泄露错误

接口泄露

内存泄露

资源泄露

未分配的指针错误

2).内存错误

动态存储溢出

无效的句柄被锁定

句柄没有被锁定

内存分配冲突

栈空间溢出

静态存储溢出

3)APIOLE错误

API函数返回失败

API函数未执行

无效的变量(包括指针变量、字符串变量等)

OLE接口方法的变量无效

OLE接口方法失败

线程调用库函数错误

 

五、           检测示例

 

5.1 内存泄漏检测示例

代码段

TempClass.cpp;

TempClass::TempClass()

{

       cout<<"构造对象TempClass"<<endl;

}

 

void TempClass::Print()

{

       cout<<"TempClass.Print()"<<endl;

}

 

#include "stdafx.h"

#include "OSTREAM.H"

#include "TempClass.h"

int main(int argc, char* argv[])

{

       printf("-----BoundsChecker用例-----------!/n");

      

       TempClass* myTempPoint = new TempClass();

       // delete myTempPoint;

       // myTempPoint = NULL;

/*

              if( NULL == myTempPoint )

              {

                     cout<<"为空"<<endl;

              }

              else

              {

                     myTempPoint->Print();

              }*/

       return 0;

}

(3)测试结果窗口

双击者点击下方的标签进入到Memory Leaks

图(4Memory Leaks窗口

图(5Memory Leak的详细资料

 

图(6)源代码窗口

结果表明在文件BoundChecker.cpp main函数的11myTempPoint已经分配分间而程序退出时发生了内存泄漏。

 

5.2 野指针检测示例

 

#include "stdafx.h"

#include "OSTREAM.H"

#include "TempClass.h"

int main(int argc, char* argv[])

{

       printf("-----BoundsChecker用例-----------!/n");

      

       TempClass* myTempPoint = new TempClass();

       delete myTempPoint;

       // myTempPoint = NULL; 

       if( NULL == myTempPoint )

       {

              cout<<"为空"<<endl;

       }

       else

       {

              myTempPoint->Print();

       }

       return 0;

}

 

 

图(7ActiveCheck模式下Debug的结果

ActiveCheck模式下是无法检查到摇摆指针的

FinalCheck检测:

(8)设置Enable FinalCheck选项

图(9)切换模式后debug目录下文件对比

图(10FinalCheck模式下Debug的结果

结果表明在main函数myTempPoint为野指针,指针所指对象已经被释放。

5.3 数组越界检测示例

       int iArTemp[8];

       for( int i = 0; i <= 8; i++ )

       {

              iArTemp[i] = i;

       }

       cout<<"打印数组数据"<<endl;

       for( i = 0; i <= 8; i++ )

       {

              cout<<iArTemp[i]<<endl;

       }

       return 0;

 

这里选择Display Error And Pause 选项,所以在Debug过程中,将会即时弹出检查出的错误信息,如下图所示。

(11)debug 时弹出的错误信息

图(12debug时弹出的错误信息

Explain 获取帮助。

Memory/Resource Viewer:查看内存和资源分配情况。

Suppress:终止某类型的报错。

Debug:切换到Debug窗口。

Halt:中断。

Continue:继续检测

Don’t Show this Error 可以屏蔽某类型错误。(下拉框可以选择条件)

Disable event Logg:是否将事件写入检测结果中。

图(13)检测结果

图(14Errors选项的结果

结果表明在main()函数中局部变量iAriTemp发生读、写越界的错误,注意在ActiveCheck 模式下是检查不到出数组越界,必需选择FinalCheck模式。

 

5.4 GDI 资源泄漏检测示例

void CGDICheckerDlg::OnPaint()

{

       CPaintDC dc(this); // device context for painting

 

       CDC pDC ;

       pDC.CreateCompatibleDC( &dc );

       CRect rc;

       GetClientRect(rc);

       CBitmap* pOldBmp = NULL;

       m_imgBk.LoadBitmap( IDB_TEST_BMP );

       pOldBmp = pDC.SelectObject( &m_imgBk );

      

       dc.BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),&pDC,0,0,SRCCOPY);      

       //     pDC.SelectObject(pOldBmp);

}

图(15GDI的测试结果

(16) GDI资源泄漏

结果表明,释放DCDC仍然占有对象的资源,同时也给出错误发生在OnPaint()函数中,对于GDI的资源泄漏也必需在FinalCheck模式下才可以检测出来。

 

5.5 句柄资源泄漏检测示例

#include "stdafx.h"

#include "windows.h"

#include "ostream.h"

DWORD WINAPI TestThread( LPVOID lpParameter );

 

int iIndex = 0;

int main(int argc, char* argv[])

{

       HANDLE hThread1;

       hThread1 = CreateThread( NULL, 0, TestThread, NULL, 0, NULL );

//     CloseHandle( hThread1 );     // Resource Leak

       while( iIndex++ < 10 )

              cout<<"main Thread is running"<<endl;

       return 0;

}

DWORD WINAPI TestThread( LPVOID lpParameter )

{

       while( iIndex++ < 10 )

              cout<<"TestThread is running"<<endl;

       return 0;

}


图(17)测试结果

结果表明程序退出时发生了资源泄漏,资源被CreateThread分配。

5.6 死锁检测示例

#include "windows.h"

#include "ostream.h"

DWORD WINAPI TestThread1( LPVOID lpParameter );

DWORD WINAPI TestThread2( LPVOID lpParameter );

 

int iTickets = 10;

CRITICAL_SECTION g_csA;

CRITICAL_SECTION g_csB;

 

int main(int argc, char* argv[])

{

       HANDLE hThread1;

       HANDLE hThread2;

       hThread1 = CreateThread( NULL, 0, TestThread1, NULL, 0, NULL );

       hThread2 = CreateThread( NULL, 0, TestThread2, NULL, 0, NULL );

       CloseHandle( hThread1 );

       CloseHandle( hThread2 );

 

       InitializeCriticalSection( &g_csA );

       InitializeCriticalSection( &g_csB );

       Sleep( 4000 );

       DeleteCriticalSection( &g_csA );

       DeleteCriticalSection( &g_csB );

      

       return 0;

}

 

DWORD WINAPI TestThread1( LPVOID lpParameter )

{

       while( TRUE )

       {

              EnterCriticalSection( &g_csA );

              Sleep(10);

              EnterCriticalSection( &g_csB );

              if( iTickets > 0 )

              {

                     Sleep(10);

                     cout<<"TestThread1 Sell Tickets: "<<iTickets--<<endl;

                     LeaveCriticalSection( &g_csB );

                     LeaveCriticalSection( &g_csA );

              }

              else

              {

                     LeaveCriticalSection( &g_csB );

                     LeaveCriticalSection( &g_csA );

                     break;

              }

       }

       return 0;

}

DWORD WINAPI TestThread2( LPVOID lpParameter )

{

       while( TRUE )

       {

              EnterCriticalSection( &g_csB );

              Sleep(1);

              EnterCriticalSection( &g_csA );

              if( iTickets > 0 )

              {

                     Sleep(1);

                     cout<<"TestThread2 Sell Tickets: "<<iTickets--<<endl;

                     LeaveCriticalSection( &g_csA );

                     LeaveCriticalSection( &g_csB );

              }

              else

              {

                     LeaveCriticalSection( &g_csA );

                     LeaveCriticalSection( &g_csB );

                     break;

              }

       }

       return 0;

}

图(18)死锁测试结果

(19) 测试结果

图(18)和图(19)中可以看出线程Thread:0x09EC 拥有边界资源Critical Section:0x00431180,同时也在等待边界资源Critical Section:0x00431120的使用权。但刚好线程Thread:0x0C74 拥有边界资源Critical Section:0x00431120 却在等待Critical Section:0x00431180的使用权,所以两个线程都在等待对方释放资源。

 

5.7 MS C-Runtime Library内建的检测功能示例

MFC封装和利用了MS C-Runtime LibraryDebug Function。非MFC程序也可以利用MS C-Runtime LibraryDebug Function加入内存泄漏的检测功能。MS C-Runtime Library在实现malloc/freestrdup等函数时已经内建了内存泄漏的检测功能。

要在非MFC程序中打开内存泄漏的检测功能非常容易,你只要在程序的入口处加入几行代码:示例如下:

#include "stdafx.h"

#include "OSTREAM.H"

#include "TempClass.h"

#include <CRTDBG.H>

int main(int argc, char* argv[])

{

       printf("-----BoundsChecker用例-----------!/n");

 

       int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );

       tmpFlag |= _CRTDBG_LEAK_CHECK_DF;

       _CrtSetDbgFlag( tmpFlag );

 

       TempClass* myTempPoint = new TempClass();

       // delete myTempPoint;

       // myTempPoint = NULL;

       return 0;

}

这样,在程序结束的时候,也就是winmainmaindllmain函数返回之后,如果还有内存块没有释放,它们的信息会被打印到Debug窗口里。

 

Detected memory leaks!

Dumping objects ->

{49} normal block at 0x00031140, 1 bytes long.

 Data: < > CD

Object dump complete.

 

 

 

 

 

 

 

 

六、           配置

可以配置属性来忽略你不感兴趣的错误方法如下:

 

6.1 Suppression

BoundsChecker菜单项中可以选择设置要禁止的报错内容,从可用的.DPsup文件中挑选要禁止的具体内容,也可以点击Add加入指定的DPsup文件。如下图所示

图(20Suppression设置框

在选择了Display Error And Pause 选项时,会在调式过程中即时弹出检查出的错误信息,如下图所示。可以点击Suppress按钮来禁止该类型的报错。当你确定要禁止该类型的报错后,BoundsCheckes之后将不再给出这类型的错误提示。

图(21Suppression设置

6.2 Filter

 

图(22)设置过滤选项

图(23)设置过滤选项

 

 

 

 

 

 

 

 

 

 

6.3设置

图(24)设置界面

 

图(25)忽略API

这样就可以忽略API的报错。

*详细设置可以点击开始->所有程序->Compuware BounderChecker->Documentation下查看BoundsChecker Quick Ref.pdf Understanding BoundsChecker.pdf文档。可以直接打开安装目录C:/Program Files/Compuware查看。

6.4 代码控制

在你不想要写入检查日志的地代码段中加入以下代码即可

#include "nmapilib.h"

       //代码被监控

       StopEvtReporting();

              //…不需要被监控的代码段

       StartEvtReporting();

       //代码被监控

*上面API必需链接NmApiLib.libC:/Program Files/Compuware/BoundsChecker/ERptApi)。

 

6.5设置应用程序关联Bounds Checker

 

以记事本为例:

打开注册表,在HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Image File Execution Options下创建notepad.exe项,将名称修改为Debugger,类型为字符串类型,值为BoundsChecker 的安装目录C:/Program Files/Compuware/BoundsChecker/bc7.exe,启动记事本,系统将会启动BoundsChecker,然后根据你的需要进行设置即可。注意在修改注册表时必需以管理员的身份。(可以查阅Understanding BoundsChecker.pdf 81页)

 

七、           注意事项

在某些情况下,我们需要忽略BoundsChecker报告的一些错误,这些情况包括:

1. 误报

BoundsChecker 指定程序中的某段代码存在错误,但经过我们的仔细检查,证实程序确实没有这个错误,这是BoundsChecker的误报。工具毕竟是工具,它只能依照为它制定的算法行事,所以会有误报的情形发生。但千万不要轻易认定某一个错误为误报,一定要对错误进行仔细的分析,确定是真正的误报。

2. 第三方的代码

BoundsChecker指定的错误发生位置在第三方提供的代码中,包括第三方提供的程序库、DLLOCX等。对于这种情况,我们也要先进行认真的检查,确定不是由于我们错误的使用第三方的代码引起的。如果最后确定不是我们的原因,则这样的错误报告可以忽略。

3. dll 检测

调试dll文件时必需由EXE文件加载,另外在设置选项Modules and Filesl选择Add Moule 来加入你想检测的dll 文件

4. 其他

还有一点需要强调,使用BoundsChecker对程序进行测试时,需要有程序的源代码。如果没有源码,BoundsChecker虽然也可以打开EXE文件将其执行起来,但得出的测试结果经常是不正确的,因此也就没有太大的意义。

      

 

八、           总结

BoundsCheck的工作流程有以下四步:

1、  配置BoundsCheck去收集你想要的数据

a、  选择你想要收集的数据

b、  定义应用程序要被监控的部分

c、  选择你想要禁止或过滤的应用

2、  运行应用程序

a、  程序运行时,错误描述会出现在Program Error Detected对话框中

b、  被禁止的错误提示部分不会提示

c、  必要是可以查看日志和创建过滤

d、  检查内存和资源的使用情况

3、  程序终止时查看数据

a、  在日志中过滤掉你不想看到的事件

b、  为将来要运行的应用程序创建一个禁止方案

4、  你可以保存当前的设置,禁止和过过滤方案以备以后使用

你可能感兴趣的:(api,测试,null,library,leak,winapi)