就像许多文章中说的那样,调试一定要冷静,要耐心。急躁解决不了问题,盲目地修改很容易添加新的bug,让问题更加复杂。
调试时需要活跃地思维,从不同角度去测试程序,观察结果,并思考为什么会这样。能把自己思考的过程写成文档记录下来是最好的。
调试不一定非要把bug揪出来,也不是对程序不能做优化。如果调试过程中发现有更好的实现方法,或者可以优化的地方,可以先优化,再调试。当然我并不建议对程序做大的改动,除非原来的方法确实很烂,有切实的测试依据。
调试过程中,时间一长,人心态再怎么好,也会急躁,心烦意乱,这时很容易错上加错。除了像开始说的冷静之外,还要使调试过程尽可能自动化。有的程序,调试起来非常麻烦,尤其是在嵌入式领域中,从修改代码到编译下载,到执行要经过好几步。如果是手工来做,很容易漏掉一步,产生出人意料的错误。 比如我有一次调试了近一天,发现bootloader加载RTOS后会跳到一个奇怪的地址去,我实在不理解是为什么。最后才发现是漏掉了将RTOS可执行文件变为binary的一步,原来的binary文件链接地址不匹配,所以就跳到了奇怪的地址。如果编译的过程是自动的,就不会出现这种问题了。
最后,如果修改过的代码出现了奇怪的错误,比如读不出数据什么的,很可能就是因为改的代码里面有错误。这时不要急着调试,先想想可能是哪里的问题,去查相应的代码。查代码的速度要远远高于调试。
调试最可怕的事,就是bug越调越多。调试时间越长,越没有心情去看代码,修改草率,脑子转不动圈,即使错误代码就摆在自己面前也看不到,非得单步运行到那里时,才发现问题所在。
后记:
调试的技巧,上面的文字也只是略窥一二。其实不同领域的调试方法天差地别,比如嵌入式的可能只能通过一个串口获取程序的打印信息,pc应用或许可以用IDE单步跟踪,web服务或数据库又可能是通过分析长篇的log...... 但不管是哪个领域的程序员,调试都是其两大基本技能之一(另一技能为编程)。调试能力主要靠经验积累和对相关程序或应用的熟悉程度。
我的经验是,绝不要写太多未经测试的代码,最好使用递增的模型,把目标程序分成多个子模块,并进一步细分,直到自己可以在1-2小时内实现,或者达到自己可以轻松完成的粒度。对于一些无法细分的模块,可以选择对模块功能进行细分,每次实现一部分。对划分的每一点,实现并测试,这样可以及时清理出现的bug。即使无法完全清除,通过这样的多次迭代,也至少可以避免所有浅层的bug。说实话,这种方法我只试过一次,是在一个只能通过串口和led灯调试的实验板上,无法单步调试或设断点,只是文档和可参考代码较多。在这种陌生而复杂的环境中,谨慎才是最重要的,需要一步一个脚印。
而在写pc上位机程序时,我更喜欢设计出所有模块的文档,一次把代码写完,再用实际应用进行测试。因为pc上的开发环境非常先进,可以设断点,单步跟踪,写log,打印,等等;这会使调试速度非常快,不需要太过小心。所以,最好根据项目的实际需要,选择合适的写代码方式和测试方案;即使不刻意,潜意识中也会这么做。
这些经验和我看过的某些编程或测试的书籍中讲的理论很贴近。书的作者们也算是苦口婆心,但那时看的印象往往很浅,只有多次亲身体会才会牢牢记住。
希望这些文字能给大家一点启发,少走一些弯路。