《Effective Debugging:软件和系统调试的66个有效方法》一第15条:查看第三方组件的源代码,以了解其用法

第15条:查看第三方组件的源代码,以了解其用法

我们所要调试的代码之所以会出bug,通常并不是由于它使用的第三方程序库或应用程序本身有问题(参见第14条),而是因为它使用这些第三方组件时所采取的方式有误。
这种情况并不令人惊讶,由于这些软件本身是作为黑盒来与你所写的代码进行集成的,因此,你不太可能在它们之间相互协调。对于这类问题来说,有一个很有用的办法,就是去查看第三方程序库、中间件甚至是底层软件的源代码。
首先,如果想查明某个API为什么没有像你所期望的那样运作,或是想查明某条奇怪的错误消息是从哪里发出来的,那么可以浏览第三方组件的源代码,并搜寻其中你所感兴趣的那一部分,以寻求答案。如果想对某个与程序库有关的功能进行调试,那么可以在第三方代码中寻找相关函数或方法的定义,然后由此继续向下查看。在查看第三方程序库的代码时,我们可能并不是要在其中寻找bug,而是想更好地理解它的工作原理以及它与你所写的代码之间的配合方式。如果想知道某条错误消息是从哪里来的,那么可以在整个代码库里面搜寻这条消息,并检查有哪些代码会发出这条消息(参见第23条和第4条)。为了能够迅速地查询函数或方法,我们可以用ctags或etags程序为代码编制索引(大多数编辑器都支持这两个程序所输出的索引),也可以采用集成开发环境(IDE)来查看代码。与ctags相比,IDE能够更好地处理复杂的语言特性,如重载、覆盖以及模板等,而ctags的优势则在于支持的语言数量比较多,5.8版本支持41种语言。在存放源代码的目录里面运行下面这条命令,可以给其中的所有文件创建索引:

如果你所使用的第三方组件是开源的,那么也可以通过Black Duck Open Hub Code Search这样的托管服务来进行搜索。
还有一种更为强大的调试技术,需要我们先构建带有调试信息的第三方组件(参见第28条),然后把自己的代码与这个调试版的第三方程序库相链接。这样,我们就可以像调试自己的代码那样,轻松地在第三方程序库的代码里面进行单步调试了,而且我们还可以使用符号调试器(symbolic debugger,参见第4章)来检视其中的变量。请注意,某些厂商(如Microsoft)在发行其代码的时候,会把供调试所用的版本或带有符号的版本也随着普通版本一起发布出来,这样我们就不用自己去构建包含调试信息的程序库了。
如果bug确实是由第三方代码而非自己的代码所引起的,那么在能够访问其源代码的前提下,你可以对此进行修改。请注意,只有在极其特殊的情况下,才需要使用这种办法,也就是说,只有在既找不到合理的权宜方案又联系不到厂商来对此进行修复的时候,才可以考虑修改第三方程序库的源代码。一旦你这么做,就必须在整个应用程序的生命期内,对该程序库的所有后续版本都做出相应的修改。此外,你还需要保证这样的改动不会违背相关的法律条款。例如,某些厂商在发布代码的时候,会在其协议中要求“只能看,不能改”(look, don’t touch)。对于开源软件来说,比较合理的一种做法,是将修改后的代码提交给负责该代码的项目,而且从道理上来讲,你也确实应该这么做。如果项目托管在GitHub上,那么只需要发起pull request就可以了。
要想运用上面提到的技术,我们必须先获取到第三方组件的源代码。如果第三方程序库或应用程序是开源的,那么很简单,只需要点一下按钮,就可以把源代码下载下来了。对于开源的操作系统发行版,还可以把源代码作为软件包来下载,例如,在Debian Linux系统中,我们可以通过以下命令安装C语言程序库的源代码:

此外,很多软件开发平台也会将其源代码中较为重要的那一部分,安装到你的系统上面。例如,Microsoft的Visual Studio会把C语言运行时库的源代码放在VC\crt\src里面,而Java Development Kit(JDK)则会把源代码放在名为src.zip的压缩文件中。对于其他一些第三方软件来说,你可以在订购软件时支付额外的费用以获取其源码。只要售价不高,我们就应该考虑这么做,若是等以后再去买,则要花很多时间来安排款项,而且还要下订单并执行所需的合约。等到那个时候,软件厂商或许已经不再支持你所使用的版本了,甚至有可能已经停业了。因此,我们应该考虑提前获取这些专有软件的源代码,以免出现此类问题。
要点
如果你依赖某个第三方组件,那么就应该获取其源代码。
通过查看第三方组件的源代码探寻与第三方API及一些奇怪的错误消息有关的问题。
要和第三方程序库的debug版本相链接。
只有当其他办法都不可行的时候,才需要对第三方的源代码进行修改。

时间: 2024-11-03 18:28:45

《Effective Debugging:软件和系统调试的66个有效方法》一第15条:查看第三方组件的源代码,以了解其用法的相关文章

《Effective Debugging:软件和系统调试的66个有效方法》——第15条:查看第三方组件的源代码,以了解其用法

第15条:查看第三方组件的源代码,以了解其用法 我们所要调试的代码之所以会出bug,通常并不是由于它使用的第三方程序库或应用程序本身有问题(参见第14条),而是因为它使用这些第三方组件时所采取的方式有误. 这种情况并不令人惊讶,由于这些软件本身是作为黑盒来与你所写的代码进行集成的,因此,你不太可能在它们之间相互协调.对于这类问题来说,有一个很有用的办法,就是去查看第三方程序库.中间件甚至是底层软件的源代码. 首先,如果想查明某个API为什么没有像你所期望的那样运作,或是想查明某条奇怪的错误消息是

《Effective Debugging:软件和系统调试的66个有效方法》——第7条:试着用多种工具构建软件,并将其放在不同的环境下执行

第7条:试着用多种工具构建软件,并将其放在不同的环境下执行 有时我们可以通过改变环境来锁定一些难以捕获的bug.例如,我们可以用另外一款编译器来构建这个软件,也可以切换到其他的运行时解释器.虚拟机.中间件.操作系统或CPU架构上.由于那些环境可能会更加严格地检查输入数据,或能通过其结构来凸现程序中的错误(参见第17条),因此可以帮助我们发现原来很难找到的一些bug.如果程序不够稳定.总是发生无法重现的崩溃问题,或移植起来不太顺利,那就应该试着把它放在另外一种环境下进行测试,这使得我们能够使用更为

《Effective Debugging:软件和系统调试的66个有效方法》——第8条:把工作焦点放在最为重要的问题上

第8条:把工作焦点放在最为重要的问题上 许多大型软件系统都含有数量极其众多的bug(有一些是已知的bug,还有一些则尚未发现).要想高效地进行调试,就必须把应该受到关注的bug与可以忽略的bug明智地区分开.这样做不是为了单纯地缩减事务清单中的未决事务,而是为了帮助我们开发出稳定.易用.可维护而且效率较高的软件,毕竟这才是公司给我们支付薪水的原因.为此,我们要通过事务追踪系统来设定各项事务的优先级(参见第1条),从而使自己能够把工作重心汇聚在优先级较高的那些事务上,并把优先级较低的事务忽略掉.下

《Effective Debugging:软件和系统调试的66个有效方法》——第6条:使用软件自身的调试机制

第6条:使用软件自身的调试机制 程序是一种很复杂的东西,因此它们通常都包含内置的调试机制.(至于怎样给自己正在开发的软件里面添加这样的机制,请参见第40条.)这种机制有很多好处,其中包括: 我们可以通过禁用后台执行或多线程执行等特性来简化程序的调试工作. 我们可以有选择地执行其中某一部分功能,以便通过测试用例来精确地再现相关的故障. 程序可以给我们提供与性能有关的报表及其他信息. 程序可以把更多的信息记录在日志文件中. 因此,我们应该花一些时间,看看自己要调试的这款软件内置了哪些调试机制.想要了

《Effective Debugging:软件和系统调试的66个有效方法》——第2条:在网上确切地查询你所遇到的问题,以寻求解决问题的灵感

第2条:在网上确切地查询你所遇到的问题,以寻求解决问题的灵感 现在很少有哪个工作场所不能上网,如果在一个无法上网的地方开发程序,那我的效率会很低.遇到代码错误的时候,我们应该上网搜索,或者与同事一起寻找解决办法. 有一个相当有效的搜索技巧,是把由第三方组件所给出的错误消息打上双引号,并将其粘贴到浏览器的搜索框里面.把待搜索的内容放在一对双引号中,意思是要告诉搜索引擎:只搜索与该内容精确匹配的页面.这样做可以使搜索结果更加准确.还有一个很有用的技巧,是把与错误有关的程序库或中间件的名称.对应的类名

《Effective Debugging:软件和系统调试的66个有效方法》——第14条:考虑对软件进行更新

第14条:考虑对软件进行更新 接下来,笔者要说几个很多人都意想不到的bug来源.并非所有错误都是由于你自己所写的代码而造成的,用来处理这些代码的编译器或解释器.你所使用的程序库.你所依赖的数据库和应用程序服务器,以及上述工具所在的操作系统,也有可能要对程序中的bug负有责任.笔者编写本书时,Linux的源代码里面包含2700多条带有XXX字样的注释,该标记通常意味着可疑的内容,这其中有一些肯定是bug. 于是,有些bug就可以通过更新软件来解决.如果你要发布的打包应用程序里面出现了隐晦的bug,

《Effective Debugging:软件和系统调试的66个有效方法》——第10条:高效地重现程序中的问题

第10条:高效地重现程序中的问题 要想高效地调试程序问题,一个关键的因素就是要能够可靠且方便地重现它.这么说有三个理由.首先,如果我们总是能做到只按一个按钮就可以重现问题,那么自然能够专心地去寻找问题的原因,而不用再浪费时间去研究怎样才能把这个问题重现一遍.第二,如果我们可以方便地重现问题,那么也就能够同样方便地把问题描述出来,以寻求外人的帮助(参见第2条).第三,修复错误之后,我们可以把重现问题所需的步骤执行一遍,如果程序这次没有出现故障,那就证明我们对其所做的修复是正确的. 创建短小的范例或

《Effective Debugging:软件和系统调试的66个有效方法》——第11条:修改完代码之后,要能够尽快看到结果

第11条:修改完代码之后,要能够尽快看到结果 调试通常是一种循序渐进的过程.在每一轮中,我们都要花时间去构建并运行软件,而且要看着它发生故障,这些环节会占用很多时间,而且这些时间并没有用来解决软件中的问题.因此,我们要提前进行准备,设法缩短每一轮调试所花费的时间. 首先从软件的构建入手.我们应该能通过一条命令(如make或mvn compile)或一个按键(如F5)把发生故障的软件迅速构建出来.构建过程应该能够记录文件之间的依赖关系,使得我们在修改了某处代码之后只有少数几个文件需要重新编译.能够

《Effective Debugging:软件和系统调试的66个有效方法》——第19条:使调试任务自动化

第19条:使调试任务自动化 我们或许会找到很多个与程序错误有关的因素,但是却没有办法轻易推断出究竟哪一个因素才是致使程序出错的真正原因.为了把这个原因找出来,我们可以编写一小段例程或脚本,把有可能使程序出错的所有情况全都搜索一遍.如果待搜索的情况比较多,不便于手工进行搜索,但是却能够通过循环来进行遍历,那么就可以考虑对其加以自动化.例如,如果想遍历的是500个字符,那么可以通过自动化的脚本来实现,然而如果要把用户可能会输入的所有字符串全都尝试一遍,那么采用自动化脚本就不太合适了. 下面举一个例子

《Effective Debugging:软件和系统调试的66个有效方法》——第16条:使用专门的监测及测试设备

第16条:使用专门的监测及测试设备 调试嵌入式系统及系统软件的时候,我们可能要对从硬件到应用程序的整个计算栈进行分析.调试工作一旦深入硬件层面,我们就需要关注电流的微小变化以及磁矩的对齐情况等细节.在大多数情况下,可以通过强大的IDE以及一些追踪软件与日志记录软件来探查这些问题,然而有的时候,就连这些工具也帮不上忙.这通常发生在软件与硬件有所接触的场合,也就是说,虽然你认为你所写的软件能够像预期的那样运作,但是硬件却有着它自己的处理方式.例如,你把正确的数据写入磁盘,再将其读取出来,却发现这些数