本节书摘来自华章计算机《Effective Debugging:软件和系统调试的66个有效方法》一书中的第1章,第6节,作者[希]迪欧米迪斯·斯宾奈里斯(Diomidis Spinellis),爱飞翔 译,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
第6条:使用软件自身的调试机制
程序是一种很复杂的东西,因此它们通常都包含内置的调试机制。(至于怎样给自己正在开发的软件里面添加这样的机制,请参见第40条。)这种机制有很多好处,其中包括:
- 我们可以通过禁用后台执行或多线程执行等特性来简化程序的调试工作。
- 我们可以有选择地执行其中某一部分功能,以便通过测试用例来精确地再现相关的故障。
- 程序可以给我们提供与性能有关的报表及其他信息。
- 程序可以把更多的信息记录在日志文件中。
因此,我们应该花一些时间,看看自己要调试的这款软件内置了哪些调试机制。想要了解这些机制,我们可以在程序文档和源代码里面搜索debug这个词,这样或许就能找到与开启调试模式有关的命令行选项、配置文件中的条目、构建选项、信号(适用于Unix系统)、注册表中的条目(适用于Windows系统)或可以在命令行界面中执行的命令。
一般来说,开启了调试选项之后,程序所输出的信息会更为详细,于是,它的操作就会变得更加明确,有时甚至还会变得更加简单。(但糟糕的是,开启了这些选项之后,有些bug也会随之消失。)由于程序的日志文件所记录的信息要比没有开启调试模式的时候更多,因此,我们可以通过这些内容来探究故障背后的原因(参见第56条)。下面举几个例子。
有些程序很容易就能通过一个命令选项来启用其调试机制,启用了之后,它在执行操作时就会给出更为详细的信息。例如,Unix shell就提供了-x选项,用来详细展示该shell所执行的命令。在调试一些比较微妙的文本替换问题时,这是很有用的。下面这段代码是一个可以用shell来执行的循环:
把上述代码放在脚本文件里,然后用开启了命令追踪机制的shell来执行它:
执行的时候,shell会产生下面这样的信息,这些信息详细展示了命令所输出的内容以及替换之后的变量:
为了能够更加顺利地进行调试,我们在启动程序的时候,通常可以把多个选项结合起来使用。例如,我们要排查ssh连接失败的原因。假如修改全局的sshd配置文件或钥匙,那么就可能会使其他人无法进行连接,因此,我们可以考虑用-f选项来启动sshd,以便指定一份定制好的配置文件,并且用-p选项来指定一个与默认端口不同的端口(请注意,客户端连接服务器时,也必须使用这个端口),此外,还要加上-d(调试)选项,使得进程运行在前台,并把调试消息显示在终端窗口中。我们分别在服务器端与客户端采用下面这两条命令来启动服务并进行连接:
这些命令运行起来之后,会产生图1.1这样的调试信息,其中的最后一条信息揭示了连接失败的原因。
调试机制也可以帮助我们排查性能方面的问题。例如,有下面这两条SQL查询语句:
第一条语句不到10毫秒就可以完成,但是第二条语句却用了3分钟还没有执行完。我们现在用SQL的explain机制来解释第二条语句耗时过长的原因(对于本例来说,这个原因当然是显而易见的):
从图1.2中的输出信息可以看到:由于第一条查询语句使用了索引,因此只需要扫描21个数据行就可以找出匹配的结果,而第二条查询语句没有使用索引,因此必须把表格里面的两亿两千二百多万个数据行全都扫描一遍,才能从中查出4个匹配的结果。
还有一种调试机制,使得我们可以精确地定位到某一种情况。例如,某台主机正在忙碌地投递上千条电子邮件消息,其中有一条消息无法投递。我们要想知道无法投递的原因,可以调用Postfix的sendmail命令,同时以-v选项来开启详细输出模式,并以-M选项来指定那条消息的标识符。
执行上述命令之后,可以看到类似下方所示的输出信息。与刚才那个例子类似,在本例中,问题的原因也写在最后一行。
要点
- 找出你正在调试的这款软件所支持的调试机制,并以此来排查你所遇到的问题。