译文:我是怎样使用BoundsChecker的

在开始本文之前,我想声明的是我曾经在NuMega工作过,并在那里参与编写了BoundsChecker的3、4、5版本。显然,我个人非常推崇BoundsChecker,尽管还会有一些其它能与BoundsChecker相媲美的产品也非常值得大家的注意,比如Rational的Purify。

作为一个终日研究调试的人,我被问到最多的问题就是,我是怎么将Compuware/NuMega BoundsChecker融入实际工作中并使用它来解决问题的。许多人都在使用或正在考虑使用BoundsChecker,但很少有人能够最大限度的使用这个复杂的产品。在这篇文章里,我将向大家介绍BoundsChecker中很容易被混淆的几个部分并向大家展示我是怎样将BoundsChecker运用的日常的开发中去的。这其中也包含了使用BoundsChecker在扩展ISAPI中进行调试,这是我遇到过的最难的部分。

如果你了解NuMega的生产线,你可能注意到他们还有另外一个产品,名字叫做SmartCheck。SmartCheck是BoundsChecker的姊妹版,用于Visual Basic的调试。这两个产品在功能上是类似的。但因为SmartCheck理解所有VB的技术,它可以用VB的语法形式显示事件以及分析VB语法中的的错误。在这篇文章中我只会使用BoundsChecker,如果你是VB的开发人员,你只需简单的把BoundsChecker替换为SmartCheck就可以了,因为这两个产品从本质上讲是一模一样的。一点主要的不同是,SmartCheck没有FinalCheck技术但BoundsChecker有。

 

拨开云雾

BoundsChecker是一个很棒的错误诊测工具,它可以发现所有的内存破坏问题,无论它们是分配在堆、静态存储区或是堆栈上。在我看来,BoundsChecker最牛的地方是它能够检查API/COM的参数。因为Windows API已经变得很大,为了不超出负荷,在不同部分之间会有一些微妙的联系。你可能会觉得GetVersionEx只是直接的数据传递,但它也会莫名的失败。OSVERSIONINFO结构中有一个很烦人的size域,你必须在把结构传给GetVweisonEx之前填充它。BoundsChecker可以很轻易的发现诸如此类的问题,你完全可以将时间和精力放在程序的特性上,而不是花大量的时间在这种愚蠢的问题上。如果想最大限度的了解BoundsChecker,你可以浏览www.numega.com上的文章。

BoundsChecker十分容易被混淆的地方是,它有两种工作模式:Active Check 和Final Check。我认为部分问题在于,人们对这两种模式在什么情况下使用存在着一些错误的说法。Active Check不需要重新编译也会为你找到大量的错误,可以以两种方式使用Active Check:可以在开发环境中使能BoundsChecker功能执行调试,也可以在BC.EXE中直接打开编译好的文件。FinalCheck需要重新编译,因为它需要将BoundsChecker的指令嵌入到工程上下文中以便随时做出精确的错误报告(比如到底在哪一行代码出现了内存泄漏)而不是象Active Check那样需要等到程序结束。有一个问题是,有些人会错误的认为从BoundsChecker得到任何错误检查都需要重新编译整个程序。当你看了我是怎样选择使用这两种模式时,你就会知道实际情况到底是什么样。

我想提出的最后一个问题是BoundsChecker的性能问题。假定,通常十分钟就可以执行完的程序用BoundsChecker却需要三个小时,这肯定是一个大问题。但我也听到一些开发人员说他们因为BoundsChecker需要多花几分钟的时间来运行整个程序所以不用BoundsChecker。如果你宁愿花2周时间来跟踪定位BoundsChecker很轻而易举就找到的问题也不愿意在运行时多花一两分钟的话我也没什么话好说。BoundsChecker能够加入对所有参数,内存,返回值以及COM接口的跟踪。通常时间是很宝贵的,能尽快找到程序的错误是最好不过的事情。如果你仅仅因为“慢”而不使用错误检查工具,就会在代码中留下大量的问题。

 

我喜欢用的的设置方法

BoundsChecker大量的选项可能就会让你忙活一阵子。别着急,我给你来个简单的。设置错误检测级别为最大模式,因为我觉得正常模式会压制一些你需要看到的错误。如果你从没使用过最大模式,你会感觉比以前更多的错误报告弹出来。不要着急,如果你不想关心存在于第三方代码中的错误的话点击suppress就好了。设置最大模式的方法是:打开VC的IDE或者BoundsChecker独立的外壳程序,BC.EXE,但不要加载任何工程或可执行文件。在BoundsChecker设置窗口的错误诊测标签下最大模式。这样以后你所打开的任何工程或文件就都会使用最大模式了。

如果我要跟踪一些非常混乱的内存问题,我就会选择自定义模式。所有其它设置都是跟最大模式相同的,但我会在“Memory error checking”选项中选中“Check all heap blocks on each memory function call”。这样一来,内存总是会被检查到,执行虽然慢一些,但错误报告会定位到更接近出错代码的地方。

默认情况下,我不会选择“Collect and report program event data”一项。当我想看到程序流程时我也只会打开事件报告。如果我必须看到所有事件,我就会将“Additional Event Reporting”里面的所有选项都选中。但有一个我通常是关掉的,就是“Event Reporting”选项卡里面的“Prompt to save program results”。因为大多时候,仅从BoundsChecker里面看一下实时的日志就可以了,没必要将它们保存起来。

使用BoundsChecker

就算NuMega的市场部要枪杀我,我还是要承认我并不是每时每刻都会使用BoundsChecker。在开发新代码或是更新现有代码时我会遵循标准的模式:写一小段代码,可能是几个函数或者是一个复杂函数的初始化部分,然后立即调试,测试这段代码。这样我就可以大体上评估一下程序的逻辑和流程。因为BoundsChecker不能发现这种类型(逻辑、流程――译著)的问题,所以我会在程序的一开始就去避免它们的出现。当我完成某个功能或是写完一段重要的代码(比如100-200行,包括注释)时,我就会打开BoundsChecker或者直接运行BC.EXE,使用ActiveCheck来测试我的这段代码。我发现,在这样的渐增的开发模式下,ActiveCheck不会发现太多的错误。实际上,如果你的工作正确无误,你不会用BoundsChecker找到任何错误。

在开发代码时,大约每天或者顶多每隔一天,我就会用BoundsChecker的Final Check来测试所有新代码。对于这样的测试版本,我会尽可能覆盖到每句代码。因为可以同时使用BoundsChecker和TrueCoverage,我通常会把两个都打开,这样我就会知道哪部分代码还需要更多的测试(没有覆盖到――译著)。

我会在把代码提交到主代码之前反复使用BoundsChecker来锤炼它们,这是很关键的。如果开发小组的每个人都这么做的话,主代码的质量就会大幅度提高。是的,这样做花费在代码测试上的功夫会超乎想象,但我认为代码质量是每个开发者的责任,我们必须做好充分的准备去保证它们的质量。

我还想告诉大家的是Final Check的使用强度问题。如果我参与的是一个很大的程序,我通常只会在我添加了代码的模块中使用Final Check。没必要将超过15个模块的30MB代码都提交给BoundsChecker。每周,我会将整个工程全部提交一次,来测试它的所有路径,这样,你就不会看到任何未知的错误。

我前面建议大家用最大诊测模式,你可能会看到更多的错误弹出。为了不让这些错误报告打断程序的运行,我经常会关掉“Report Errors Immediately”。运行完之后,再从头到尾查看错误信息。错误经常会分布在很多不同模块中,而有一些是没有源代码的。比如,当我使用Intellipoint软件时,它会调用到MSH_ZWF.DLL来处理鼠标滚轮的相关功能。当在BoundsChecker打开执行这个程序时,会弹出一些属于DLL的错误。显然,所有出自MSH_ZWF.DLL的错误都是我想压制的。我可以通过下面的方法来做到这一点:在这个错误上右击,弹出菜单选择“Suppress”在弹出的对话框中选择“Suppress this error only when it occurs in the EXE or DLL”。任何类型的错误,只要它是出现在没有代码的模块中的,就可以首先检查这个错误是否是传递错误的参数造成的,如果排除了这一点,通常就可以安全的将这个错误压制掉。我最关心的错误其实是在我所拥有代码中出现的错误,并且这些错误是我写入的代码导致的。

对于那些并非出自我代码中的错误,我会认真的评估一下。如果BoundsChecker报告的是API调用失败,我会看一下我是否恰当的检验了返回值,如果是这样,我就会将错误压制掉。当遇到内存泄漏的错误时,我会更加小心的进行检查。有时候,当应用程序调用ExitProcess后,BoundsChecker的注入DLL,BCCORE.DLL会先于程序的其它部分退出。在这种情况下,BoundsChecker为了安全起见,会将所有内存分配都看作内存泄漏。所以当静态类分配内存或资源时,你会经常看到内存或资源泄漏,这是因为静态类是进程中最后被释放的东西。这种情况下,如果你能确保会恰当的释放内存和资源,将这些错误压制也是安全的。

我是个妄想狂,我经常会把工程的错误压制文件(.SUP)更名,来检查一下我是否将真正的错误也失手压制了。工程的.SUP文件和可执行文件放在同一个目录下。如果你是一个项目小组的一员,你很可能会想将自己的.SUP文件整合到整个项目的.SUP文件中去。这个很容易,因为.SUP文件只是一个文本文件。你只要保证不要更改.SUP的开头部分(如下面所示)然后将自己.SUP文件中以“ignore”开头的每一行拷贝到项目.SUP文件中就可以了。但要注意,每一行都是以“;”结尾的。

//SUPPRESSIONPROJ:wordpad
//VERSION:5.00
//ENABLE:Yes
!include Mfc.sup ; NOTE!!include's lines are part of the header.
 
ignore failure USER32.DLL:ShowCaret in module RICHED20.DLL
ignore param 1 GDI32.DLL:GetDeviceCaps in module RICHED20.DLL
ignore param 1 USER32.DLL:GetSystemMetrics in module CSCUI.DLL
ignore param 1 KERNEL32.DLL:VerifyVersionInfoW in module CSCUI.DLL
ignore failure KERNEL32.DLL:VerifyVersionInfoW in module CSCUI.DLL

为了减轻我在错误压制上面做的繁琐工作,我会将没有代码的DLL加入到主.SUP文件中去。主.SUP文件在BoundsChecker安装目录的/Data目录下,SKIP_32C.SUP是Windows 9x下使用的,SKIP_NT.SUP是NT/2000下使用的。这两个文件里已经包含了很多DLL,但你可能拥有一些第三方库,而你不想看到这些库中的错误报告。比如,如果我想将MSH_ZWF.DLL加进主.SUP中,我可以打开SKIP_NT.SUP,在文件的底部加入一行:

ignore everything in module MSH_ZWF.DLL

在扩展的ISAPI中使用BoundsChecker

现在每个人都准备建立下一个amazon.com,所以似乎每个人都会在自己的web站点中使用ISPAI扩展。但调试扩展ISAPI是十分困难的,而且想要使用内存诊测工具更困难。幸运的是,我发现了一些小技巧,使使用内存诊测工具变得非常简单。正如你所知道的,可以以提示符模式运行IIS(MSDN有一篇文章“TN063: Debuging Internet Extension DLLs”)。这使我们的调试变得相对比较简单,但想使内存诊测工具能正确报告内存泄漏还需要一些其它的设置。

我的技巧就是,在扩展DLL中加入个特殊的命令,比如“killIIS”,这个命令可以在浏览器中调用。命令响应函数所作的仅仅是调用ExitProcess。这样就可以在VC++中使用BoundsChecker得到错误信息了。尽管以命令提示符模式执行IIS还存在一些问题,但如果能彻底的进行错误检查,这样做也是值得的。

小结

这篇文章中,我只是粗浅的介绍了一下BoundsChecker的强大功能。还有一个地方我没有介绍到,就是你可以在BoundsChecker加入自己的函数参数检查策略,当你的程序由于调用了某些API而不能在其它版本的Windows上使用时BoundsChecker就会报告这个错误。我希望大家从这篇文章中得到了一些使用BoundsChecker的好的思路,并帮助你们加快程序调试的速度。以后的专栏中,我会简单介绍一下逆向工程技术,所以请大家想一想BoundsChecker在逆向工程的用处。

时间: 2017-10-09

译文:我是怎样使用BoundsChecker的的相关文章

Matt DeBoard:我是如何成为程序员的

Matt DeBoard本是一名退伍军人.他在12周的时间内,由一名非程序员成长为一名专业开发者.对于如何成为一名程序员?他有着自己独到的心得与学习经验.他在博文<How I Became a Programmer>中介绍了自己成为一名程序员的学习经历,并通过问答形式,分享了很多学习经验和心得.CSDN对该文进行了编译,译文如下: 我昨天在"HackerNews"上发表了一个简短的回应--有关于反对"Ruby on Rails"的八星期指导课能够令你成为

我是如何找回 Reddit 密码的

本文讲的是我是如何找回 Reddit 密码的, 原文地址:That time I had to crack my own Reddit password 原文作者:本文已获原作者 Haseeb Qureshi 授权 译文出自:掘金翻译计划 译者:cdpath 校对者:atuooo (oOatuo), yzgyyang (Guangyuan (Charlie) Yang) 我是如何找回 Reddit 密码的 黑掉整个星球,伙计们! 我真是一点自制力都没有. 好在我对这一点颇有自知之明.我有意识地筹

JavaScript闭包 懂不懂由你反正我是懂了_javascript技巧

越来越觉得国内没有教书育人的氛围,为了弄懂JS的闭包,我使出了我英语四级吃奶的劲去google上搜寻着有关闭包的解释,当我看到stackoverflow上这一篇解答,我脑中就出现了一句话:就是这货没跑了! 不才译文见下,见笑了. Peter Mortensen问: 就像老Albert所说的,"如果你不能向一个六岁的孩子解释清楚,那么其实你自己根本就没弄懂."好吧,我试着向一个27岁的朋友就是JS闭包(JavaScript closure)却彻底失败了. 你们会怎么把它解释给一个充满好奇

[译] 我是如何在谷歌做开发者用户体验的

本文讲的是[译] 我是如何在谷歌做开发者用户体验的, 原文地址:How I do Developer UX at Google 原文作者:Tao Dong 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:Lai 校对者:临书 Cherry 基于 Flutter 的用户调研进行说明 人们谈论用户体验(UX)时,谈论的对象通常是他们所热爱的消费产品,比如:智能手机.消息应用或者一副耳机. 但是当你为开发者构建产品时,用户体验同样也很重要.人们往往会忘记开发

[译] 我是如何做到在 5 分钟之内将应用大小减少 60% 的

本文讲的是[译] 我是如何做到在 5 分钟之内将应用大小减少 60% 的, 原文地址:How you can decrease application size by 60% (In only 5 minutes)? 原文作者:本文已获作者 Keval Patel 授权 译文出自:掘金翻译计划 译者:jifaxu 校对者:gaozp, ZiXYu 我是如何做到在 5 分钟之内将应用大小减少 60% 的 移动设备的资源总是有限的.有限的电量,有限的存储,有限的处理能力,有限的内存,有限的网络带宽-

java后台有啥好书看,我是学技术流的

问题描述 java后台有啥好书看,我是学技术流的 java后台有啥好书看,我是学技术流的,想多看看书,还有数据库, 解决方案 基本的:重构 改善既有代码的设计,headfirst设计模式 高级的:Java并发编程实践,深入java虚拟机 这些都是最根本的. 有些网站上的文章可以多看看,都很有帮助,比如ifeve.com,上面有不错的文章和译文. 解决方案二: http://bbs.csdn.net/topics/380228140 解决方案三: 想学技术,去学c吧. 解决方案四: Java方面的

我是个学生,求大神指教!!!

问题描述 我是个学生,求大神指教!!! 题目是:有n个整数,使前面各数顺序向后移m个位置,最后m个数变成最前面m个数,写一个函数实现以上功能#includeint input(int *p){ int i=0; do { scanf(""%d""p+i); i++; }while(*(p+i-1)!=-1);return i-1; }int* mv(int nint mint p){ int ib[100]*c=NULL; for(i=0;i<m;i++) {

我是新手,JAVA 怎么理解 中的+5+

问题描述 我是新手,JAVA 怎么理解 中的+5+ 我是新手,JAVA中 System.out.printl("the absoulute of"+5+" is "+5) 怎么理解 中的+5+ 解决方案 System.out.println("the absoulute of"+5+" is "+5) 因为+5+的前面是字符串,所以会自动将5转换成字符串"5"连接到"the absoulute o

我是一个混蛋程序员

我最近收到了这封来自Artsicle公司创始人的邮件,我大一时整个夏天都在这家创业公司里实习. 来自:Scott Carleton 主题:刚刚重构了你一年前写的 find_art.js 脚本 从某些方面上看,我心里想:你是个相当混蛋的程序员. 但从另一方面上讲,真扯蛋,这个脚本在过去的20个月里完美的运行,根本不需要我去碰它.:) 本文的作者: Dan Shipper Scott说的一点没错:我是个混蛋程序员.我不认真的注释我的代码.有时,我会违反DRY编程原则.我不喜欢使用奇妙的三重操作符表达

pb 串行通讯-急求,大家好,我是PB软件的新手,在使用MSCOMM控件编写的时候遇到问题

问题描述 急求,大家好,我是PB软件的新手,在使用MSCOMM控件编写的时候遇到问题 在使用MSCOMM控件编写oncomm程序的时候,显示错误:coo31:syntax error string Is_input,Out_input choose case Ole_MSC.object.commevent case 2 //CommEvent属性为CommEvReceive Ole_MSC.object.InputLen=10000 //读入缓冲区中的数据长度 Is_input=Ole_MSC