忧郁的大能猫
好奇的探索者,理性的思考者,踏实的行动者。
Table of Contents:
调试心理学
去解决问题,而不是责备
不要恐慌bug,永远去发掘问题的根本原因,而不仅仅停留在问题的表面现象
理解系统
即在调试之前,需要先搞明白系统的运作方式。包括并不限于阅读文档,阅读源码,寻找熟悉系统的同事交流。
理解了你自己的系统后,还会获得一个额外的好处。当你找到bug时,必须在不破坏其他地方的前提下修复它们。理解系统行为是不破坏系统的第一步。理解了系统之后,你会明白什么是对的,什么是错的——
稳定复现bug
为了修复 bug,我们需要找到引发 bug 的条件,确保可以有规律的重现 bug,接下来才是修复:
当问题没有修复时,如果你执行 X 操作,失败率为100%;在修复问题后,再执行 X 操作,如果失败率为0,那么你知道bug确实已被修复。
为了有规律的重现 bug,我们应该想办法制造/增加 bug 出现的条件。如果车胎漏气,我们可以把车胎放在肥皂水里,寻找气泡。
分而治之,缩小范围
通过反复地把问题分成好的一半和坏的一半,来缩小搜索范围,然后进一步研究有问题的那一半。
控制变量法,一次只改一个地方
1.这叫控制变量法,每次只控制一个变量才能确定次变量对系统的影响
2.如果你所做的更改没有起到预期的作用,那么就把它改回来。它们可能会产生无法预料的影响。
检查插头(Check the Plug)
当电器出现问题的时候,应该问自己一个古老的、看似愚蠢的问题:“插头插上了吗?”虽然这个问题看上去很愚蠢,但它经常发生。
它的核心思想是:在花几个小时研究复杂问题之前,先排查那些最简单、最容易被忽略的原因。
很多经验丰富的工程师遇到问题时,第一步往往不是看代码,而是先问:
1. 服务启动了吗?
2. 请求发出去了吗?
3. 请求到达了吗?
4. 参数对吗?
5. 配置对吗?
6. 环境对吗?
7. 日志对吗?
8. 数据真的存在吗?
80% 的 Bug 都不是算法错了,而是“插头没插好”——配置、环境、权限、路径、参数、缓存、拼写等基础问题出了错。
寻求他人帮助
与其自己与 bug 死扛,不如去寻求同事的帮助:
别人寻求帮助至少有3个原因(还不算把整个问题甩给别人):获得全新观点、专业知识和经验。而且,人们通常很愿意帮忙,因为这给了他们一个证明自己很聪明的机会。
我们按照自己老一套的思路是很难看清全局的。我们都是普通人,对任何事情都有偏见,包括对bug隐藏在哪里的看法。这些偏见可能导致我们无法看清实际情况。而其他人则会从另一个角度来看问题,这可能会给我们很大的启发,帮助找到新的方法。
即使无法从他们那里得到帮助,他们也可以安慰你一下,告诉你这个问题真是一个非常棘手的问题,也可以借给你肩膀靠一靠。
无论你想要获得什么样的帮助,在向别人描述问题的时候,一定要记住一件事:报告症状,而不要讲你的理论。之所以要从别人那里获得全新的观点,就是因为你的理论起不到任何作用。如果你找了一个人,把你的理论告诉他,那么也会把他拉到你原来的思维定式中。
如果你不修复bug,它将依然存在
当你认为你已经修复了一个设计问题时,取消这个修复,确定系统再次失败。然后再应用这个修复,再次验证问题已修复。
log("last login time : %d", last_login_time);
不知道某个函数是如何被调用的,可以打个断点,然后查看调用堆栈
断点
调试器遇到断点时会把程序暂时挂起,让程序进入一种特殊的状态—中断状态,这种状态下操作系统不会终止程序的执行,也不会清除与程序相关的元素,比如变量、函数等,它们在内存中的位置不会发生变化。
关键是,处于中断状态下的程序允许用户查看和修改它的运行状态,比如查看和修改变量的值、查看和修改内存中的数据、查看函数调用关系等,这就是调试的奥秘。
条件断点(conditional breakpoints)
a==b ,然后在下面选择 为 true ,然后点击确定即可。数据断点(Data breakpoints)
Hit count
添加监视(Watch)
单步调试
修改代码运行位置
即时窗口的使用
“即时窗口”是VS提供的一项非常强大的功能,在调试模式下,我们可以在即时窗口中输入C语言代码并立即运行
在即时窗口中可以使用代码中的变量,可以输出变量或表达式的值,也可以修改变量的值,还可以调用函数。
即时窗口本质上是一个命令解释器,它负责解释我们输入的代码,再由VS中的对应模块执行,最后将输出结果呈现到即时窗口。
需要注意的是,在即时窗口中不能定义新的变量,因为程序运行时 Windows 已经为它分配好了只够刚好使用的内存,定义变量是需要额外分配内存的,所以调试器不允许在程序运行的过程中定义变量,因为这可能会导致不可预知的后果。
内存查看窗口(查看、修改运行时的内存)
菜单 -> 调试(D) -> 窗口 (W) -> 内存 (M) -> 内存1(1),就可以调出内存窗口了
默认的内存是没有格式的,所以我们应该以四字节的形式格式化这些内存数据(假设是查看int型数据的),这时我们在内存窗口中单击我们的鼠标右键,在弹出的菜单中选择“4字节整数(4)”
assert()断言函数,在assert.h头文件中定义
查程序崩溃
Qt下程序崩溃,但是没有崩溃时的堆栈
若出现崩溃错误,一定要消灭在萌芽中,不可放任不管,因为在刚发现时,往往知道是修改那个功能出现的崩溃,拖得越久,越难以确定崩溃的范围
查找步骤:
1. 查找稳定复现的方法
2. 在代码中加打印,从大范围,到小范围确定出问题的代码段
3. 锁定可疑的代码,如系统调用,容器越界,容器删除等操作
4. 修改可疑代码,验证是否解决