Augusdi的专栏

攀登技术的高峰,我与大家共勉!

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、  你可以保存当前的设置,禁止和过过滤方案以备以后使用

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭