什么是测试?——发现问题。
什么是调试?——定位问题原因并解决问题。
最近项目中遇到了几个“疑难杂症”,在问题排查过程中,经验丰富的老司机级开发,也一次次掉到坑里、再爬出来,然后掉下一个坑…… 在经历了一番艰辛的排查之后,终于定位到了问题根因。这时开发和测试同学发现:如果我们一开始采用正确的问题排查方法,就能更快找到问题原因。
痛定思痛,本文特此总结一下疑难问题(简单问题可以忽略)的问题排查技巧,仅供各位开发人员参考,同时测试人员也可以据此思考如何支持开发快速排查问题。毕竟测试发现问题的最终目的,是能够快速解决它。
软件调试的基本思路
1、强行排错法(Brute-force Method)
这种方法需要动脑筋的地方比较少,因此叫强行排错。通常有以下三种形式:
① 打印内存变量的值:在执行程序时,通过打印内存变量的数值,将该数值同预期的数值进行比较,判断程序是否出错。对于小型程序,这种方法很有效,逻辑关系复杂的大型程序则效果较差。
② 在程序关键分支处设置断点(如弹出提示框):这种方法对于弄清多分支程序的流向很有帮助,可以很快锁定程序出错发生的大概位置范围。
③ 使用编程软件的调试工具:通常IDE集成开发环境都有调试功能,使用单步调试功能可以一步步跟踪程序执行流程,以便发现错误所在。
2、回溯排错法(Backtracking)
这是在小型程序中常用的一种有效的调试方法。一旦发现了错误,可以先分析错误现象,确定最先发现该错误的位置。然后,从错误出现之处出发,沿反向路径进行检查,直到找出错误原因。
3、归纳排错法(Debugging by Induction)
归纳法是从特殊推断一般的思考方法。归纳法调试的基本思想是,从错误现象和数据等线索着手,研究错误和数据规律(可能需要补充对比测试),分析关联模块、各种因素、各种输入来设计假说,分别试验来证明或排除假说,最终找到错误原因。
4、演绎排错法(Debugging by Deduction)
演绎法是从一般原理出发,经过排除和锁定的过程来推导结论的思考方法。调试时,首先根据错误现象,枚举出所有可能出错的原因作为假设,然后再使用数据逐个验证排除错误假设,将余下的原因作为主攻方向。
软件调试的规则
(摘自《调试九法:软硬件错误的排查之道》)
规则1:理解系统
你必须掌握系统的工作原理和设计实现,你没有理解的某个部分通常是出问题的地方(墨菲定律的原理)。
如何理解系统?方法包括:阅读手册理解细节、知道什么是正常的(这样才能注意到什么是不正常的)、理解业务流程、不要轻信经验或记忆(经验会骗人、记忆会出错)。
规则2:制造失败
排查问题的过程中,最重要的一步就是复现问题——能复现的问题都能解决。
注意:Debug动作不要影响错误的发生方式或概率、观察从正常状态到不正常的状态的过程、控制变量法依次排除各种因素(剩下那个答案,无论多么不可思议,都是事实)。
规则3:不要猜,而要看
观察方法包括设置断点、添加调试语句、监视程序值以及检查内存(类似医学领域的验血和X光透视)。
持续观察细节,直到把问题原因锁定在几种可能之内。
在系统设计时,就要考虑后续调试、排查问题的情况,将日志视为系统设计的一部分。
日常生活中的插桩案例:体温计测量体温、自行车轮胎放在水里检查漏气点。
规则4:二分法
反复将问题分成好的一半和坏的一半,然后缩小搜索范围,然后进一步研究有问题的那一半链路。
规则5:一次只改一个地方
初中就学过的控制变量法。
在修改bug时,如果某个改动没有修复bug,就应该立即把它改回来。
规则6:保持审计跟踪
记下你的每步操作、顺序和结果;
魔鬼藏在细节中;
将一些事情关联起来思考;
好记性不如烂笔头(记录问题排查过程的阶段性结论),我也经常给开发人员讲“排查问题就像警察破案,排查过程要有卷宗记录,便于随时回看或者其他人查阅”。
规则7:检查插头
(注:这本书里的很多案例都是硬件相关的,但道理是相通的)
一些显而易见的假设可能是错误的;是不是运行了正确的代码?是不是打了正确的包?插头是不是掉了?从一些最基本的问题开始确认,很多时候问题就出在这里。对自己使用的工具进行测试,因为工具也是一种软件,难保不会出问题。
规则8:获得全新观点
“要想重新理清一个案子的头绪,最好的方法就是把它讲给别人听。” ——福尔摩斯《银色马》
向别人解释问题的过程,会让你对问题进行重新的梳理和理解,这时候可能发现之前没有发现的问题。
Bug发生了,以除掉bug为自豪,而不是非得以自己除掉bug才自豪。
不管你是跟什么人求助,或者需要别人什么样的帮助(征求意见、获取专业知识、听取经验),在向别人描述问题时,一定要记住一件事——报告症状、而不是讲你的理论;另外,有些症状你可能不是十分确定,也可以描述出来。
规则9:如果你不修复bug,它将依然存在
“当危险已经离你很近时,拒绝承认它并不是勇敢的表现,而是愚蠢。” ——福尔摩斯《最后一案》
如果你不修复bug,它不会自动消失。按照前面的规则解决问题后,要进行一次回归验证,确保已经修复问题,并且没有引入新的问题。
软件调试常用方法
1、重现问题
这一步非常重要,如果可以,尽可能找到问题的必现步骤。即使问题很难重现,能找到出现规律和概率对于解决问题也很有帮助。
2、缩小范围
① 二分法
通过断点或打印标记,将问题区域代码从中间隔开,如果有停止执行或者输出标记时仍未遇到错误,说明错误在下半部,反之便在上半部。除了针对程序代码,二分法也可用于软件版本,某个版本出现了问题,找到前面无此问题的版本,然后在多个版本之间采用二分法,快速找到首次出现问题的版本,也会有助于定位问题原因。