软件除错

原创 2003年01月03日 11:12:00

加入Sybase不久,一位久未谋面的朋友问我在做什么。我说软件修理工(我在Sybase维护PowerBuilder)。说这话时,我丝毫没有贬低修理工的意思,相反,我从小就羡慕出色的修理工。不响的收音机,他们捣鼓捣鼓就响了;不干活的机器,他们鼓捣鼓捣就干活了。

 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

一名出色的除错(debugging)高手是公司的宝贵财富。有一次,有人问公司的一位高级副总裁:谁是他手下最重要的人。回答是:两名高级工程师,因为有些错只有他们能解决。在我看来,除错不光是一种谋生手段,还是一种益智游戏。所以,在查错时,千万不要惊慌,要静下心来,享受破案的乐趣。

 

除错在软件开发与维护中占很大比重。能否尽早、尽快、高质量地除错关系到软件的成败与否。一个开发高手必定是除错高手。除错基本上分成两部分:找出错误原因和改正错误。怎样才能快速定位错误原因呢?

. 熟悉你的工具

工欲善其事,必先利其器。这句成语大家虽然耳熟能详,但我觉得咱们中国人好象更崇尚不需要任何工具就能办大事的人,如用一根线就能给人号脉的神医。但能达到此种境界的究竟有几人呢?更何况,他们如果有合适的工具,不是能干得更快更好吗?

 

有了好工具,还要挖掘其功能。以Visual Studio而言,它提供了强大的除错功能。全面掌握其功能,将大大缩短除错周期。

 

断点在除错中扮演至关重要的角色。合理的设置断点,能让你事半功倍。最常用的断点是位置断点。程序运行到断点时即刻停止。这时,你可以检查变量的值,查看内存的内容等等。寄希望于看到一些蛛丝马迹。

1.1条件断点

有时,程序要经过某断点很多次才能到达你所希望的现场。来回按F5键(Go)简直是不胜其烦。幸好Visual Studio允许我们通过断点对话框为位置断点设置条件。只有在所设的条件成立时,该断点才起作用。断点的条件是一个布尔表达式,例如:a==100||(b>10&&c<1.5)。其中a, b, c可以是局部变量、成员变量、也可以是全局变量。

 

需要注意的是条件表达式中不能有函数调用,包括重载的操作符。那么,既然不能用strcmp函数,能否让条件断点在某一字符串变量为某一特定值时起作用呢?能。假设你有一个字符串变量str,并希望在str等于abc时断点起作用,你可用如下条件表达式:str[0]==’a’&&str[1]==’b’&&str[2]==’c’

 

你还可以让某一位置断点跳过若干次后才起作用。假设你正在排除一个GPF。你设置了一个位置断点,并希望查看GPF发生之前断点处的情况。但你不知道要按F5键多少次才能到达你所希望的现场。即使你知道按多少次,你也不想按那么次。幸好,Visual Studio允许一个位置断点在跳过若干次以后才起作用。你可先让断点跳过1000次,然后执行程序。死机后,看断点到底跳过多少次才死机。用该次数重新设置断点的跳跃次数。再执行程序时,程序就会在死机前的一刹那停下来。

1.2数据断点

在某些场合下,数据断点会成为你的救星。在查错时,如果你发现某个数据被莫名其妙地该掉了,怎样才能查出该数据在何时何地被改掉的呢?用数据断点。通过断点对话框,给出该数据的地址及长度。以后,在任何代码改变该数据的值时,程序就会马上停下来,让你分析该变化是否合理。

1.3 Stop always

如果你的程序利用了C++的异常处理功能,在跟踪时,你有可能发现程序突然跑到一个很远的catch语句去了。那是因为C++的异常处理功能起作用了。在程序正常运行时,异常处理功能是你的朋友,但在跟踪时,可就未必了,因为你不知道程序到底在什么地方出错了。幸好Visual Studio允许你在跟踪时取消异常处理功能。选择Debug—Exceptions菜单,在弹出的对话框中,改变调试器(Debugger)在某一类异常发生时所采取的行动(Action)。你可将行动设置成Stop always,只要该类异常发生时,程序马上停下来。

 

Visual Studio还提供各种窗口,让你检查程序调用堆栈、变量、积存器和内存等。

1.4其它工具

除了Visual Studio,还要一些很有用的工具,如Windows NT操作系统提供的Performance MonitorVisual Studio的附带工具Spy++、以及www.sysinternals.com提供的HandleExFileMonRegMon等等。熟练掌握这些工具将对你大有裨益。

2. 熟悉你的程序

有了像样的工具并不能使你马上成为除错高手。一个不懂得机器工作原理的修理工,即使有再好的工具,也很难快速并高质量地修理机器。为什么在一些大项目中,有些错只有某几个人才能排除呢?主要原因是只有他们才更全面地了解他们的程序。

 

要想成为真正的除错高手,你要主动出击,理解程序的总体结构,熟悉代码。怎样才能理解程序总体结构呢?怎样才算理解了程序总体结构呢?如果你有完备的文档,那算你幸运。如果没有,那只有凭自己了。我个人的经验是画UML图。有道是“一张图顶一千句话”。看一眼组件图(Component Diagram),马上就能想到整个程序有哪几块组成;看一眼类图(Class Diagram),马上就能回想起这几个类之间的关系;看一眼时序图(Sequence Diagram),马上就能想起这几个对象是如何交互的。画图的工具可以是Rational Rose,也可以是Sybase PowerDesignerMicrosoft Visio,甚至可以是PowerPoint。然后用一个Word文件把这些图串起来,check in到源文件控制系统(Source Control System),以便随时补充修改。

 

没有紧急任务时,我会借助代码分析软件,如SourceNavigatorSource Insight,或Object Outline等,概略分析源代码(而不是逐行阅读),补充所缺的图。有时,我也会有意识地跟踪程序,逐个研究函数调用堆栈(call stack)中的每个函数,找出类之间的关系并记录在册。值得注意的是一定要把你的发现记录在册,否则几个月后,你可能要重新来过。好记性不如烂笔头子。

 

另外,查错的过程也是一个很好的理解源代码的机会。对那些有重要意义的call stack,我都会记录下来,以便有空时研究。日积月累,你会发现你不但对总体结构有了深刻理解,而且也掌握了相当多的细节。到这时,查错就不再是大海捞针,也不是走迷宫,因为你已经有了一张地形图。

3. 管理的功用

熟练掌握了先进的工具,又对程序有了相当程度的理解,你已经具备了成为除错高手的条件。而良好的软件管理能使你事半功倍。软件管理基本上包括:

 

l         代码控制系统

l         质量跟踪系统

l         软件发布及存档系统

3.1代码控制系统

代码控制系统有诸多好处:防止代码意外丢失、能保证代码同步、允许回溯到开发过程中的任一点、允许多个工程师同时修改同一个文件等等。由于每一个修改都有记录,能促使工程师提高程序质量,以免被别人笑话。另外,代码控制系统在除错中也有重要意义。下面将有详述。

3.2质量跟踪系统

Sybase有一套相当严格的质量跟踪系统。质量控制工程师、技术支持工程师、甚至是开发工程师都可在质量跟踪系统中输入新的错误报告(Sybase称之为change request)。每个错误报告都需有详细的说明、严重程度、错误复现(reproduce)步骤以及相应的test case等。Test case要尽量小。

 

每个产品有一个小组,由技术支持经理负责,每周挑选出最严重的错误,并通知相应的开发小组组长。组长再把错误分给相应的工程师。该工程师找出错误原因,提出解决方案。方案复查通过后,将经修改的代码check in到代码控制系统。同时把错误报告转交给质量控制小组。质量控制小组在确认错误被解决后,关闭该错误报告。

3.3软件发布及存档系统

程序的每一个重要的build都要存档,可随时下载运行。

4. 一些实用技巧

查错很重要的一环是设置断点。断点位置选得好能让你更快地查出错误的原因。这里有一个小技巧:如果错误的表现是程序弹出了一个对话框显示某一出错信息,你可以启动Visual Studioattach到你的程序,然后选择Debug—Break菜单暂停程序运行。从函数调用堆栈上,也许会有所发现。如果程序陷入死循环,也可以使用这一技巧。如果错误的表现是死机,那就更容易找到切入点了。

 

在调试多线程(multi-threading)程序时,把程序运行情况写入文件中(logging)是一个常用的方法。在你认为有可能出现问题的函数中,把重要数据写入文件中。问题出现后,仔细研究该文件,试图发现一些蛛丝马迹。逐渐缩小范围,知道找出问题之所在。

 

内存泄漏(memory leak)是C++程序的常见问题。检查内存泄漏问题最好要有工具。通常我会先用Performance Monitor证明程序确实发生了泄漏。然后用BoundsChecker或其它工具帮忙确定内存泄漏的根源。

 

有时程序会发生退化(regression),即某一功能突然不工作了。如果不能快速查出原因,我们通常会采用折半查找(binary search)的方法找出究竟是哪个change list惹的祸。方法是先找出从哪一个build开始出现问题,然后再找出是哪个change list。假设build 1000工作正常,build 1010出现了问题。我们就从程序存档中下载build 1005,看是否有问题。如果有问题,就下载build 1003,否则下载build 1007。依此类推,直到找出那个build为止。然后从代码控制系统中查出这个build与上一个build之间有那些change list,再次采用折半查找的方法。方法是把程序同步(synchronize)到某个change list,编译并运行程序,看是否有问题。如此这般,直到找出引起问题的change list。把check inchange list的同事找来一起研究问题的原因并加以解决。

 

最另人头痛的错误是那些随机发生、很难再现的错误。这些错误通常是由于变量未赋初值,或内存被破坏等原因造成的。如果程序发生随机的死机现象,你可以利用Windows操作系统提供的SetUnhandledExceptionFilter 函数设置你自己的未处理异常过滤器(unhandled exception filter)。当程序发生未被处理的异常时,Windows会自动调用过滤器函数。过滤器函数中,你可以把程序运行现场的情况,如函数调用堆栈、寄存器的值等,写入一个文件以便分析。如果你的发布版本(release build)中带有最基本的调试信息,你就能在函数调用堆栈中看到真实的函数名称,而不是密码一般的函数地址。如果release build中没有任何调试信息,你可从编译源文件时生成的map文件中查出究竟是在运行哪一行代码时出现了问题。所以如果你不想让release build带调试信息,你一定要把map文件存档,以备不日之需。

 

如果你招数用尽,仍无法查出错误的根源。你不妨请同事帮忙,或调用外部力量。俗话说:没有过不了的火焰山。只要你不泄气、群策群力,一定能解决问题。毕竟,计算机是讲逻辑的。

 

错误解决后,在大松一口气之前,你最好总结一下查错的过程及心得。如果你的程序有单元测试程序(unit test),你应该增加一些单元测试程序,防止类似情况再次发生。如果你的程序有自动集成测试,你也应加入相应的测试。

 

另外,与其被动等错误来找你,不如主动出击。方法之一是进行代码复查(code review)。选出某个文件,由小组成员分头复查该文件,然后集体讨论,找出所有错误及有待提高的地方。代码复查不仅能防患于未然,还是提高编程水平的好招。

 

态度在除错中也扮演着重要角色,尤其是在承受着很大的压力时。由于这个错误软件不能发布,定单被搁置。但你一定要保持冷静。“这肯定是编译器的错”,“这肯定是操作系统的错”,“这肯定是第三方软件的错”等等。说这些话都是不冷静的。你要“拿证据来”。总之,不要怨天尤人。是自己的错,勇于承认。是别人的错,切莫讥讽。

 

掌握了方法,又有良好的态度,何愁不能成为除错高手?

 

5. 参考资料:

Debugging Applications

MSDNMicrosoft

UML DistilledMartin Fowler

RefactoringMartin Fowler

 

去除eclipse的web项目下标一把叉的问题

问题描述:用maven构建项目,总是发现左下角有一把叉,如图所示 点开各个文件,没有发现编译错误,仔细核对发现web.xml中用的版本号是3.0, ...
  • qq_25011941
  • qq_25011941
  • 2016年09月14日 11:35
  • 583

软件调试排错除错工具篇

1、事先说明:如果你软件设计整体架构比较优秀,而且软件本身又比较健全的排错机制,大部分情况都可以通过现象和排错日志快速定位出来,那么恭喜你,减少了很多维护软件bug而产生的多余死人的工作量。2、排错除...
  • fishmai
  • fishmai
  • 2016年09月01日 12:51
  • 451

除错触发

begin if old.unit_price insert into r_order_product_update(update_time,order_id,product_id,old_pri...
  • kingdtl
  • kingdtl
  • 2014年02月08日 17:22
  • 243

查错 除错 Debugging

加入Sybase不久,一位久未谋面的朋友问我在做什么。我说软件修理工(我在Sybase维护PowerBuilder)。说这话时,我丝毫没有贬低修理工的意思,相反,我从小就羡慕出色的修理工。不响的收音机...
  • pnking
  • pnking
  • 2006年11月07日 09:25
  • 737

DM9161AEP常见问题,除错指南

1.晶振部分,不管是MII的25M,还是RMII的50M,输出一定要50ppm,对于DM9161AEP,最好  接XT1,对于DM9161CEP,接XT1,XT2都可以。   2.pin31-LEDM...
  • LOV2031
  • LOV2031
  • 2015年07月13日 09:40
  • 661

《完美软件》笔记5:测试与除错的区别

“正确的定义可以防止或者结束争论。” –Nathanael Emmons,神学家   在引人注目的“测试”标题下,往往囊括了很多需要不同技能的工作。这样的笼统做法影响了计划、估算、工作分配,甚至会...
  • DavyYew
  • DavyYew
  • 2010年03月11日 00:23
  • 205

Yii-- DeleteAll连表删除报错问题的解决方法

删除数据的时候,经常会遇到连联判断删除数据的条件,今天用Yii 的CDbCriteria生成关连条件。批量删除的时候数据库报错。 页面代码为$criteria=new CDbCriteria; $c...
  • dreamzml
  • dreamzml
  • 2013年07月04日 18:06
  • 4654

大型系统中除错新思路

 大型系统中除错新思路最近公司在线系统中经常性地发现一个Bug, 会导致数据库中一个核心表的数据发生混乱, 而因为这是个核心业务表,会有成千上万个存储过程,Stored Func,甚至直接是SQL语句...
  • rainxy2000
  • rainxy2000
  • 2009年02月18日 17:32
  • 216

debugger:除错,安全监控,优化,修改

调试即是对指令流顺序进行跟踪和干预,对码流内容进行识别的工具,用来分析程序的行为过程和目的。调试工具依赖具体系统,x86或者java虚拟机,或者其他执行系统。 1、windows sdk中包含win...
  • uhml
  • uhml
  • 2014年04月04日 16:55
  • 609

使用Delphi整合除错环境

这一次我们来练习抓虫虫!程式设计师绝对都曾经有过与「臭虫」奋战的经验,你是否也曾经为了一个程式的臭虫忙到深夜後来发现问题出在一个实在很蠢的地方?优异的开发环境如Delphi者当然不会忘记除错器(Deb...
  • guxianga
  • guxianga
  • 2007年09月01日 16:53
  • 856
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:软件除错
举报原因:
原因补充:

(最多只允许输入30个字)